#ifdef __unix__
/* Needed for popen function */
#define _POSIX_C_SOURCE 200112L
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#ifndef _WIN32
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#else
#include <io.h>
#endif

#ifdef __APPLE__
#include <crt_externs.h>
#endif

#include "gks.h"
#include "gkscore.h"

#ifndef R_OK
#define R_OK 4
#endif

#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif

#define nint(a) ((int)(a + 0.5))

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define LEFT (1 << 0)
#define RIGHT (1 << 1)
#define BOTTOM (1 << 2)
#define TOP (1 << 3)

#define WC_to_NDC(xw, yw, tnr, xn, yn)     \
  xn = gkss->a[tnr] * (xw) + gkss->b[tnr]; \
  yn = gkss->c[tnr] * (yw) + gkss->d[tnr]

#define NDC_to_WC(xn, yn, tnr, xw, yw)     \
  xw = ((xn)-gkss->b[tnr]) / gkss->a[tnr]; \
  yw = ((yn)-gkss->d[tnr]) / gkss->c[tnr]

#define WC_to_NDC_rel(xw, yw, tnr, xn, yn) \
  xn = gkss->a[tnr] * (xw);                \
  yn = gkss->c[tnr] * (yw)

#ifndef _WIN32
enum tmux_state_t
{
  NO_TMUX = 0,
  SINGLE_TMUX_SESSION,
  NESTED_TMUX_SESSION
};
#endif

struct wstypes_t
{
  const char *name;
  int wstype;
};

static struct wstypes_t wstypes[] = {
    {"win", 41},       {"ps", 62},      {"eps", 62},       {"nul", 100},      {"pdf", 102},      {"mov", 120},
    {"gif", 130},      {"apng", 131},   {"cairopng", 140}, {"cairox11", 141}, {"cairojpg", 144}, {"cairobmp", 145},
    {"cairotif", 146}, {"six", 150},    {"iterm", 151},    {"mp4", 160},      {"webm", 161},     {"ogg", 162},
    {"x11", 211},      {"pgf", 314},    {"bmp", 145},      {"jpeg", 144},     {"jpg", 144},      {"png", 140},
    {"tiff", 146},     {"tif", 146},    {"gtk", 142},      {"wx", 380},       {"qt", 381},       {"svg", 382},
    {"wmf", 390},      {"quartz", 400}, {"socket", 410},   {"sock", 410},     {"gksqt", 411},    {"qtcairo", 412},
    {"qtagg", 413},    {"zmq", 415},    {"gl", 420},       {"opengl", 420},   {"ppm", 170}};

static int num_wstypes = sizeof(wstypes) / sizeof(wstypes[0]);

static int pattern[120][33] = {
    {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 255, 255, 187, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 238, 255, 187, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 187, 238, 187, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 119, 221, 119, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 119, 170, 119, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 85, 170, 85, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 85, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 136, 85, 136, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 136, 34, 136, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 0, 68, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {4, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 128, 0, 8, 0, 128, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 34, 136, 34, 136, 34, 136, 34, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 170, 85, 170, 85, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 170, 0, 170, 0, 170, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 85, 85, 85, 85, 85, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 17, 34, 68, 136, 17, 34, 68, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 119, 119, 119, 119, 119, 119, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 78, 207, 252, 228, 39, 63, 243, 114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 239, 253, 223, 254, 247, 191, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 119, 119, 119, 0, 119, 119, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 127, 127, 127, 0, 247, 247, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 191, 223, 255, 253, 251, 247, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 125, 187, 198, 187, 125, 254, 254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 7, 139, 221, 184, 112, 232, 221, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 170, 95, 191, 191, 170, 245, 251, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 223, 175, 119, 119, 119, 119, 250, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 64, 255, 64, 64, 79, 79, 79, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 255, 247, 255, 127, 255, 247, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 255, 221, 255, 119, 255, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 221, 119, 221, 119, 221, 119, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 85, 255, 85, 255, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 255, 0, 255, 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 238, 221, 187, 119, 238, 221, 187, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 254, 253, 251, 247, 239, 223, 191, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 127, 255, 119, 255, 127, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 247, 227, 221, 62, 127, 254, 253, 251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 235, 221, 190, 119, 255, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 191, 95, 255, 255, 251, 245, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 252, 123, 183, 207, 243, 253, 254, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 127, 190, 193, 247, 247, 235, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 239, 223, 171, 85, 0, 253, 251, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 136, 118, 112, 112, 136, 103, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 255, 247, 235, 213, 170, 213, 235, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 255, 247, 235, 213, 170, 213, 235, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 255, 255, 255, 247, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 255, 255, 255, 247, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 255, 255, 255, 119, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 255, 223, 255, 119, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 255, 223, 255, 119, 255, 253, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 255, 221, 255, 119, 255, 253, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 255, 221, 255, 119, 255, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 87, 255, 221, 255, 119, 255, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 87, 255, 221, 255, 117, 255, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 221, 255, 117, 255, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 221, 255, 85, 255, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 93, 255, 85, 255, 221, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 93, 255, 85, 255, 213, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 85, 255, 85, 255, 213, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 255, 85, 255, 85, 255, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 191, 85, 255, 85, 255, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 191, 85, 255, 85, 251, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 187, 85, 255, 85, 251, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 187, 85, 255, 85, 187, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 187, 85, 239, 85, 187, 85, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 187, 85, 239, 85, 187, 85, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 187, 85, 238, 85, 187, 85, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 187, 85, 238, 85, 187, 85, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 171, 85, 238, 85, 187, 85, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 171, 85, 238, 85, 186, 85, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 170, 85, 238, 85, 186, 85, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 170, 85, 238, 85, 170, 85, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 170, 85, 174, 85, 170, 85, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 170, 85, 174, 85, 170, 85, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 170, 85, 170, 85, 170, 85, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 85, 170, 85, 170, 85, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 21, 170, 85, 170, 85, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 21, 170, 85, 170, 81, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 17, 170, 85, 170, 81, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 17, 170, 85, 170, 17, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 17, 170, 69, 170, 17, 170, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 17, 170, 69, 170, 17, 170, 84, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 17, 170, 68, 170, 17, 170, 84, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 17, 170, 68, 170, 17, 170, 68, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 1, 170, 68, 170, 17, 170, 68, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 1, 170, 68, 170, 16, 170, 68, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 170, 68, 170, 16, 170, 68, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 170, 68, 170, 0, 170, 68, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 170, 4, 170, 0, 170, 68, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 170, 4, 170, 0, 170, 64, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 170, 0, 170, 0, 170, 64, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 170, 0, 170, 0, 170, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 42, 0, 170, 0, 170, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 42, 0, 170, 0, 162, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 34, 0, 170, 0, 162, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 34, 0, 170, 0, 34, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 34, 0, 138, 0, 34, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 34, 0, 138, 0, 34, 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 34, 0, 136, 0, 34, 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 34, 0, 136, 0, 34, 0, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 2, 0, 136, 0, 34, 0, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 2, 0, 136, 0, 32, 0, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 0, 0, 136, 0, 32, 0, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 119, 119, 119, 119, 119, 119, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 119, 187, 221, 238, 119, 187, 221, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 238, 221, 187, 119, 238, 221, 187, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 0, 119, 119, 119, 0, 119, 119, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 126, 189, 219, 231, 231, 219, 189, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 247, 247, 247, 247, 247, 247, 247, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 255, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 127, 191, 223, 239, 247, 251, 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 254, 253, 251, 247, 239, 223, 191, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {8, 247, 247, 247, 247, 0, 247, 247, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};

static double rgb[MAX_COLOR][3] = {
    {1.00000, 1.00000, 1.00000}, {0.00000, 0.00000, 0.00000}, {1.00000, 0.00000, 0.00000}, {0.00000, 1.00000, 0.00000},
    {0.00000, 0.00000, 1.00000}, {0.00000, 1.00000, 1.00000}, {1.00000, 1.00000, 0.00000}, {1.00000, 0.00000, 1.00000},
    {0.12500, 0.12500, 0.87500}, {0.18750, 0.12500, 0.87500}, {0.25000, 0.12500, 0.87500}, {0.31250, 0.12500, 0.87500},
    {0.37500, 0.12500, 0.87500}, {0.43750, 0.12500, 0.87500}, {0.50000, 0.12500, 0.87500}, {0.56250, 0.12500, 0.87500},
    {0.62500, 0.12500, 0.87500}, {0.68750, 0.12500, 0.87500}, {0.75000, 0.12500, 0.87500}, {0.81250, 0.12500, 0.87500},
    {0.87500, 0.12500, 0.87500}, {0.87500, 0.12500, 0.81250}, {0.87500, 0.12500, 0.75000}, {0.87500, 0.12500, 0.68750},
    {0.87500, 0.12500, 0.62500}, {0.87500, 0.12500, 0.56250}, {0.87500, 0.12500, 0.50000}, {0.87500, 0.12500, 0.43750},
    {0.87500, 0.12500, 0.37500}, {0.87500, 0.12500, 0.31250}, {0.87500, 0.12500, 0.25000}, {0.87500, 0.12500, 0.18750},
    {0.87500, 0.12500, 0.12500}, {0.87500, 0.18750, 0.12500}, {0.87500, 0.25000, 0.12500}, {0.87500, 0.31250, 0.12500},
    {0.87500, 0.37500, 0.12500}, {0.87500, 0.43750, 0.12500}, {0.87500, 0.50000, 0.12500}, {0.87500, 0.56250, 0.12500},
    {0.87500, 0.62500, 0.12500}, {0.87500, 0.68750, 0.12500}, {0.87500, 0.75000, 0.12500}, {0.87500, 0.81250, 0.12500},
    {0.87500, 0.87500, 0.12500}, {0.81250, 0.87500, 0.12500}, {0.75000, 0.87500, 0.12500}, {0.68750, 0.87500, 0.12500},
    {0.62500, 0.87500, 0.12500}, {0.56250, 0.87500, 0.12500}, {0.50000, 0.87500, 0.12500}, {0.43750, 0.87500, 0.12500},
    {0.37500, 0.87500, 0.12500}, {0.31250, 0.87500, 0.12500}, {0.25000, 0.87500, 0.12500}, {0.18750, 0.87500, 0.12500},
    {0.12500, 0.87500, 0.12500}, {0.12500, 0.87500, 0.18750}, {0.12500, 0.87500, 0.25000}, {0.12500, 0.87500, 0.31250},
    {0.12500, 0.87500, 0.37500}, {0.12500, 0.87500, 0.43750}, {0.12500, 0.87500, 0.50000}, {0.12500, 0.87500, 0.56250},
    {0.12500, 0.87500, 0.62500}, {0.12500, 0.87500, 0.68750}, {0.12500, 0.87500, 0.75000}, {0.12500, 0.87500, 0.81250},
    {0.12500, 0.87500, 0.87500}, {0.12500, 0.81250, 0.87500}, {0.12500, 0.75000, 0.87500}, {0.12500, 0.68750, 0.87500},
    {0.12500, 0.62500, 0.87500}, {0.12500, 0.56250, 0.87500}, {0.12500, 0.50000, 0.87500}, {0.12500, 0.43750, 0.87500},
    {0.12500, 0.37500, 0.87500}, {0.12500, 0.31250, 0.87500}, {0.12500, 0.25000, 0.87500}, {0.12500, 0.18750, 0.87500},
    {0.26667, 0.26667, 0.26667}, {0.33333, 0.33333, 0.33333}, {0.40000, 0.40000, 0.40000}, {0.46667, 0.46667, 0.46667},
    {0.53333, 0.53333, 0.53333}, {0.60000, 0.60000, 0.60000}, {0.66667, 0.66667, 0.66667}, {0.73333, 0.73333, 0.73333},
    {0.80000, 0.80000, 0.80000}, {0.86667, 0.86667, 0.86667}, {0.93333, 0.93333, 0.93333}, {1.00000, 1.00000, 1.00000},
    {0.05000, 0.20000, 0.35000}, {0.06667, 0.23333, 0.46667}, {0.08333, 0.25000, 0.58333}, {0.10000, 0.25000, 0.70000},
    {0.11667, 0.23333, 0.81667}, {0.18333, 0.24167, 0.88333}, {0.30000, 0.30000, 0.90000}, {0.45833, 0.41667, 0.91667},
    {0.60000, 0.53333, 0.93333}, {0.72500, 0.65000, 0.95000}, {0.83333, 0.76667, 0.96667}, {0.92500, 0.88333, 0.98333},
    {0.20000, 0.05000, 0.35000}, {0.30000, 0.06667, 0.46667}, {0.41667, 0.08333, 0.58333}, {0.55000, 0.10000, 0.70000},
    {0.70000, 0.11667, 0.81667}, {0.82500, 0.18333, 0.88333}, {0.90000, 0.30000, 0.90000}, {0.91667, 0.41667, 0.87500},
    {0.93333, 0.53333, 0.86667}, {0.95000, 0.65000, 0.87500}, {0.96667, 0.76667, 0.90000}, {0.98333, 0.88333, 0.94167},
    {0.35000, 0.05000, 0.20000}, {0.46667, 0.06667, 0.24000}, {0.58333, 0.08333, 0.25000}, {0.70000, 0.10000, 0.25000},
    {0.81667, 0.11667, 0.23333}, {0.88333, 0.18333, 0.24167}, {0.90000, 0.30000, 0.30000}, {0.91667, 0.45833, 0.41667},
    {0.93333, 0.60000, 0.53333}, {0.95000, 0.72500, 0.65000}, {0.96667, 0.83333, 0.76667}, {0.98333, 0.92500, 0.88333},
    {0.35000, 0.20000, 0.05000}, {0.46667, 0.30000, 0.06667}, {0.58333, 0.41667, 0.08333}, {0.70000, 0.55000, 0.10000},
    {0.81667, 0.70000, 0.11667}, {0.88333, 0.82500, 0.18333}, {0.90000, 0.90000, 0.30000}, {0.87500, 0.91667, 0.41667},
    {0.86667, 0.93333, 0.53333}, {0.87500, 0.95000, 0.65000}, {0.90000, 0.96667, 0.76667}, {0.94167, 0.98333, 0.88333},
    {0.20000, 0.35000, 0.05000}, {0.23333, 0.46667, 0.06667}, {0.25833, 0.58333, 0.08333}, {0.25000, 0.70000, 0.10000},
    {0.23333, 0.81667, 0.11667}, {0.24167, 0.88333, 0.18333}, {0.30000, 0.90000, 0.30000}, {0.41667, 0.91667, 0.45833},
    {0.53333, 0.93333, 0.60000}, {0.65000, 0.95000, 0.72500}, {0.76667, 0.96667, 0.83333}, {0.88333, 0.98333, 0.92500},
    {0.05000, 0.35000, 0.20000}, {0.06667, 0.46667, 0.30000}, {0.08333, 0.58333, 0.41667}, {0.10000, 0.70000, 0.55000},
    {0.11667, 0.81667, 0.70000}, {0.18333, 0.88333, 0.82500}, {0.30000, 0.90000, 0.90000}, {0.41667, 0.87500, 0.91667},
    {0.53333, 0.86667, 0.93333}, {0.65000, 0.87500, 0.95000}, {0.76667, 0.90000, 0.96667}, {0.88333, 0.94167, 0.98333},
    {0.09800, 0.09800, 0.43920}, {0.00000, 0.00000, 0.50200}, {0.39220, 0.58430, 0.92940}, {0.28240, 0.23920, 0.54510},
    {0.41570, 0.35290, 0.80390}, {0.48240, 0.40780, 0.93330}, {0.51760, 0.43920, 1.00000}, {0.00000, 0.00000, 0.80390},
    {0.25490, 0.41180, 0.88240}, {0.11760, 0.56470, 1.00000}, {0.00000, 0.74900, 1.00000}, {0.52940, 0.80780, 0.92160},
    {0.52940, 0.80780, 0.98040}, {0.27450, 0.50980, 0.70590}, {0.69020, 0.76860, 0.87060}, {0.67840, 0.84710, 0.90200},
    {0.69020, 0.87840, 0.90200}, {0.68630, 0.93330, 0.93330}, {0.00000, 0.80780, 0.81960}, {0.28240, 0.81960, 0.80000},
    {0.25100, 0.87840, 0.81570}, {0.87840, 1.00000, 1.00000}, {0.37250, 0.61960, 0.62750}, {0.40000, 0.80390, 0.66670},
    {0.49800, 1.00000, 0.83140}, {0.00000, 0.39220, 0.00000}, {0.33330, 0.41960, 0.18430}, {0.56080, 0.73730, 0.56080},
    {0.18040, 0.54510, 0.34120}, {0.23530, 0.70200, 0.44310}, {0.12550, 0.69800, 0.66670}, {0.59610, 0.98430, 0.59610},
    {0.00000, 1.00000, 0.49800}, {0.48630, 0.98820, 0.00000}, {0.49800, 1.00000, 0.00000}, {0.00000, 0.98040, 0.60390},
    {0.67840, 1.00000, 0.18430}, {0.19610, 0.80390, 0.19610}, {0.60390, 0.80390, 0.19610}, {0.13330, 0.54510, 0.13330},
    {0.41960, 0.55690, 0.13730}, {0.74120, 0.71760, 0.41960}, {0.94120, 0.90200, 0.54900}, {0.93330, 0.90980, 0.66670},
    {0.98040, 0.98040, 0.82350}, {1.00000, 1.00000, 0.87840}, {1.00000, 0.84310, 0.00000}, {0.93330, 0.86670, 0.50980},
    {0.85490, 0.64710, 0.12550}, {0.72160, 0.52550, 0.04310}, {0.73730, 0.56080, 0.56080}, {0.80390, 0.36080, 0.36080},
    {0.54510, 0.27060, 0.07450}, {0.62750, 0.32160, 0.17650}, {0.80390, 0.52160, 0.24710}, {0.87060, 0.72160, 0.52940},
    {0.96080, 0.96080, 0.86270}, {0.96080, 0.87060, 0.70200}, {0.95690, 0.64310, 0.37650}, {0.82350, 0.70590, 0.54900},
    {0.82350, 0.41180, 0.11760}, {0.69800, 0.13330, 0.13330}, {0.64710, 0.16470, 0.16470}, {0.91370, 0.58820, 0.47840},
    {0.98040, 0.50200, 0.44710}, {1.00000, 0.62750, 0.47840}, {1.00000, 0.64710, 0.00000}, {1.00000, 0.54900, 0.00000},
    {1.00000, 0.49800, 0.31370}, {0.94120, 0.50200, 0.50200}, {1.00000, 0.38820, 0.27840}, {1.00000, 0.27060, 0.00000},
    {1.00000, 0.41180, 0.70590}, {1.00000, 0.07840, 0.57650}, {1.00000, 0.75290, 0.79610}, {1.00000, 0.71370, 0.75690},
    {0.85880, 0.43920, 0.57650}, {0.69020, 0.18820, 0.37650}, {0.78040, 0.08240, 0.52160}, {0.81570, 0.12550, 0.56470},
    {0.93330, 0.50980, 0.93330}, {0.86670, 0.62750, 0.86670}, {0.85490, 0.43920, 0.83920}, {0.72940, 0.33330, 0.82750},
    {0.60000, 0.19610, 0.80000}, {0.58040, 0.00000, 0.82750}, {0.54120, 0.16860, 0.88630}, {0.62750, 0.12550, 0.94120},
    {0.57650, 0.43920, 0.85880}, {0.84710, 0.74900, 0.84710}, {0.25000, 0.25000, 0.25000}, {0.50000, 0.50000, 0.50000},
    {0.75000, 0.75000, 0.75000}, {0.00000, 0.00000, 1.00000}, {0.01818, 0.00000, 1.00000}, {0.03636, 0.00000, 1.00000},
    {0.05455, 0.00000, 1.00000}, {0.07273, 0.00000, 1.00000}, {0.09091, 0.00000, 1.00000}, {0.10909, 0.00000, 1.00000},
    {0.12727, 0.00000, 1.00000}, {0.14545, 0.00000, 1.00000}, {0.16364, 0.00000, 1.00000}, {0.18182, 0.00000, 1.00000},
    {0.20000, 0.00000, 1.00000}, {0.21818, 0.00000, 1.00000}, {0.23636, 0.00000, 1.00000}, {0.25455, 0.00000, 1.00000},
    {0.27273, 0.00000, 1.00000}, {0.29091, 0.00000, 1.00000}, {0.30909, 0.00000, 1.00000}, {0.32727, 0.00000, 1.00000},
    {0.34545, 0.00000, 1.00000}, {0.36364, 0.00000, 1.00000}, {0.38182, 0.00000, 1.00000}, {0.40000, 0.00000, 1.00000},
    {0.41818, 0.00000, 1.00000}, {0.43636, 0.00000, 1.00000}, {0.45455, 0.00000, 1.00000}, {0.47273, 0.00000, 1.00000},
    {0.49091, 0.00000, 1.00000}, {0.50909, 0.00000, 1.00000}, {0.52727, 0.00000, 1.00000}, {0.54545, 0.00000, 1.00000},
    {0.56364, 0.00000, 1.00000}, {0.58182, 0.00000, 1.00000}, {0.60000, 0.00000, 1.00000}, {0.61818, 0.00000, 1.00000},
    {0.63636, 0.00000, 1.00000}, {0.65454, 0.00000, 1.00000}, {0.67273, 0.00000, 1.00000}, {0.69091, 0.00000, 1.00000},
    {0.70909, 0.00000, 1.00000}, {0.72727, 0.00000, 1.00000}, {0.74545, 0.00000, 1.00000}, {0.76364, 0.00000, 1.00000},
    {0.78182, 0.00000, 1.00000}, {0.80000, 0.00000, 1.00000}, {0.81818, 0.00000, 1.00000}, {0.83636, 0.00000, 1.00000},
    {0.85454, 0.00000, 1.00000}, {0.87273, 0.00000, 1.00000}, {0.89091, 0.00000, 1.00000}, {0.90909, 0.00000, 1.00000},
    {0.92727, 0.00000, 1.00000}, {0.94545, 0.00000, 1.00000}, {0.96364, 0.00000, 1.00000}, {0.98182, 0.00000, 1.00000},
    {1.00000, 0.00000, 1.00000}, {1.00000, 0.00000, 0.98182}, {1.00000, 0.00000, 0.96364}, {1.00000, 0.00000, 0.94545},
    {1.00000, 0.00000, 0.92727}, {1.00000, 0.00000, 0.90909}, {1.00000, 0.00000, 0.89091}, {1.00000, 0.00000, 0.87273},
    {1.00000, 0.00000, 0.85455}, {1.00000, 0.00000, 0.83636}, {1.00000, 0.00000, 0.81818}, {1.00000, 0.00000, 0.80000},
    {1.00000, 0.00000, 0.78182}, {1.00000, 0.00000, 0.76364}, {1.00000, 0.00000, 0.74545}, {1.00000, 0.00000, 0.72727},
    {1.00000, 0.00000, 0.70909}, {1.00000, 0.00000, 0.69091}, {1.00000, 0.00000, 0.67273}, {1.00000, 0.00000, 0.65455},
    {1.00000, 0.00000, 0.63636}, {1.00000, 0.00000, 0.61818}, {1.00000, 0.00000, 0.60000}, {1.00000, 0.00000, 0.58182},
    {1.00000, 0.00000, 0.56364}, {1.00000, 0.00000, 0.54545}, {1.00000, 0.00000, 0.52727}, {1.00000, 0.00000, 0.50909},
    {1.00000, 0.00000, 0.49091}, {1.00000, 0.00000, 0.47273}, {1.00000, 0.00000, 0.45455}, {1.00000, 0.00000, 0.43636},
    {1.00000, 0.00000, 0.41818}, {1.00000, 0.00000, 0.40000}, {1.00000, 0.00000, 0.38182}, {1.00000, 0.00000, 0.36364},
    {1.00000, 0.00000, 0.34546}, {1.00000, 0.00000, 0.32727}, {1.00000, 0.00000, 0.30909}, {1.00000, 0.00000, 0.29091},
    {1.00000, 0.00000, 0.27273}, {1.00000, 0.00000, 0.25455}, {1.00000, 0.00000, 0.23636}, {1.00000, 0.00000, 0.21818},
    {1.00000, 0.00000, 0.20000}, {1.00000, 0.00000, 0.18182}, {1.00000, 0.00000, 0.16364}, {1.00000, 0.00000, 0.14546},
    {1.00000, 0.00000, 0.12727}, {1.00000, 0.00000, 0.10909}, {1.00000, 0.00000, 0.09091}, {1.00000, 0.00000, 0.07273},
    {1.00000, 0.00000, 0.05455}, {1.00000, 0.00000, 0.03636}, {1.00000, 0.00000, 0.01818}, {1.00000, 0.00000, 0.00000},
    {1.00000, 0.01818, 0.00000}, {1.00000, 0.03636, 0.00000}, {1.00000, 0.05455, 0.00000}, {1.00000, 0.07273, 0.00000},
    {1.00000, 0.09091, 0.00000}, {1.00000, 0.10909, 0.00000}, {1.00000, 0.12727, 0.00000}, {1.00000, 0.14545, 0.00000},
    {1.00000, 0.16364, 0.00000}, {1.00000, 0.18182, 0.00000}, {1.00000, 0.20000, 0.00000}, {1.00000, 0.21818, 0.00000},
    {1.00000, 0.23636, 0.00000}, {1.00000, 0.25455, 0.00000}, {1.00000, 0.27273, 0.00000}, {1.00000, 0.29091, 0.00000},
    {1.00000, 0.30909, 0.00000}, {1.00000, 0.32727, 0.00000}, {1.00000, 0.34545, 0.00000}, {1.00000, 0.36364, 0.00000},
    {1.00000, 0.38182, 0.00000}, {1.00000, 0.40000, 0.00000}, {1.00000, 0.41818, 0.00000}, {1.00000, 0.43636, 0.00000},
    {1.00000, 0.45455, 0.00000}, {1.00000, 0.47273, 0.00000}, {1.00000, 0.49091, 0.00000}, {1.00000, 0.50909, 0.00000},
    {1.00000, 0.52727, 0.00000}, {1.00000, 0.54545, 0.00000}, {1.00000, 0.56364, 0.00000}, {1.00000, 0.58182, 0.00000},
    {1.00000, 0.60000, 0.00000}, {1.00000, 0.61818, 0.00000}, {1.00000, 0.63636, 0.00000}, {1.00000, 0.65454, 0.00000},
    {1.00000, 0.67273, 0.00000}, {1.00000, 0.69091, 0.00000}, {1.00000, 0.70909, 0.00000}, {1.00000, 0.72727, 0.00000},
    {1.00000, 0.74545, 0.00000}, {1.00000, 0.76364, 0.00000}, {1.00000, 0.78182, 0.00000}, {1.00000, 0.80000, 0.00000},
    {1.00000, 0.81818, 0.00000}, {1.00000, 0.83636, 0.00000}, {1.00000, 0.85454, 0.00000}, {1.00000, 0.87273, 0.00000},
    {1.00000, 0.89091, 0.00000}, {1.00000, 0.90909, 0.00000}, {1.00000, 0.92727, 0.00000}, {1.00000, 0.94545, 0.00000},
    {1.00000, 0.96364, 0.00000}, {1.00000, 0.98182, 0.00000}, {1.00000, 1.00000, 0.00000}, {0.98182, 1.00000, 0.00000},
    {0.96364, 1.00000, 0.00000}, {0.94545, 1.00000, 0.00000}, {0.92727, 1.00000, 0.00000}, {0.90909, 1.00000, 0.00000},
    {0.89091, 1.00000, 0.00000}, {0.87273, 1.00000, 0.00000}, {0.85455, 1.00000, 0.00000}, {0.83636, 1.00000, 0.00000},
    {0.81818, 1.00000, 0.00000}, {0.80000, 1.00000, 0.00000}, {0.78182, 1.00000, 0.00000}, {0.76364, 1.00000, 0.00000},
    {0.74545, 1.00000, 0.00000}, {0.72727, 1.00000, 0.00000}, {0.70909, 1.00000, 0.00000}, {0.69091, 1.00000, 0.00000},
    {0.67273, 1.00000, 0.00000}, {0.65455, 1.00000, 0.00000}, {0.63636, 1.00000, 0.00000}, {0.61818, 1.00000, 0.00000},
    {0.60000, 1.00000, 0.00000}, {0.58182, 1.00000, 0.00000}, {0.56364, 1.00000, 0.00000}, {0.54545, 1.00000, 0.00000},
    {0.52727, 1.00000, 0.00000}, {0.50909, 1.00000, 0.00000}, {0.49091, 1.00000, 0.00000}, {0.47273, 1.00000, 0.00000},
    {0.45455, 1.00000, 0.00000}, {0.43636, 1.00000, 0.00000}, {0.41818, 1.00000, 0.00000}, {0.40000, 1.00000, 0.00000},
    {0.38182, 1.00000, 0.00000}, {0.36364, 1.00000, 0.00000}, {0.34546, 1.00000, 0.00000}, {0.32727, 1.00000, 0.00000},
    {0.30909, 1.00000, 0.00000}, {0.29091, 1.00000, 0.00000}, {0.27273, 1.00000, 0.00000}, {0.25455, 1.00000, 0.00000},
    {0.23636, 1.00000, 0.00000}, {0.21818, 1.00000, 0.00000}, {0.20000, 1.00000, 0.00000}, {0.18182, 1.00000, 0.00000},
    {0.16364, 1.00000, 0.00000}, {0.14546, 1.00000, 0.00000}, {0.12727, 1.00000, 0.00000}, {0.10909, 1.00000, 0.00000},
    {0.09091, 1.00000, 0.00000}, {0.07273, 1.00000, 0.00000}, {0.05455, 1.00000, 0.00000}, {0.03636, 1.00000, 0.00000},
    {0.01818, 1.00000, 0.00000}, {0.00000, 1.00000, 0.00000}, {0.00000, 1.00000, 0.01818}, {0.00000, 1.00000, 0.03636},
    {0.00000, 1.00000, 0.05455}, {0.00000, 1.00000, 0.07273}, {0.00000, 1.00000, 0.09091}, {0.00000, 1.00000, 0.10909},
    {0.00000, 1.00000, 0.12727}, {0.00000, 1.00000, 0.14545}, {0.00000, 1.00000, 0.16364}, {0.00000, 1.00000, 0.18182},
    {0.00000, 1.00000, 0.20000}, {0.00000, 1.00000, 0.21818}, {0.00000, 1.00000, 0.23636}, {0.00000, 1.00000, 0.25455},
    {0.00000, 1.00000, 0.27273}, {0.00000, 1.00000, 0.29091}, {0.00000, 1.00000, 0.30909}, {0.00000, 1.00000, 0.32727},
    {0.00000, 1.00000, 0.34545}, {0.00000, 1.00000, 0.36364}, {0.00000, 1.00000, 0.38182}, {0.00000, 1.00000, 0.40000},
    {0.00000, 1.00000, 0.41818}, {0.00000, 1.00000, 0.43636}, {0.00000, 1.00000, 0.45455}, {0.00000, 1.00000, 0.47273},
    {0.00000, 1.00000, 0.49091}, {0.00000, 1.00000, 0.50909}, {0.00000, 1.00000, 0.52727}, {0.00000, 1.00000, 0.54545},
    {0.00000, 1.00000, 0.56364}, {0.00000, 1.00000, 0.58182}, {0.00000, 1.00000, 0.60000}, {0.00000, 1.00000, 0.61818},
    {0.00000, 1.00000, 0.63636}, {0.00000, 1.00000, 0.65454}, {0.00000, 1.00000, 0.67273}, {0.00000, 1.00000, 0.69091},
    {0.00000, 1.00000, 0.70909}, {0.00000, 1.00000, 0.72727}, {0.00000, 1.00000, 0.74545}, {0.00000, 1.00000, 0.76364},
    {0.00000, 1.00000, 0.78182}, {0.00000, 1.00000, 0.80000}, {0.00000, 1.00000, 0.81818}, {0.00000, 1.00000, 0.83636},
    {0.00000, 1.00000, 0.85454}, {0.00000, 1.00000, 0.87273}, {0.00000, 1.00000, 0.89091}, {0.00000, 1.00000, 0.90909},
    {0.00000, 1.00000, 0.92727}, {0.00000, 1.00000, 0.94545}, {0.00000, 1.00000, 0.96364}, {0.00000, 1.00000, 0.98182},
    {0.00000, 1.00000, 1.00000}, {0.00000, 0.98182, 1.00000}, {0.00000, 0.96364, 1.00000}, {0.00000, 0.94545, 1.00000},
    {0.00000, 0.92727, 1.00000}, {0.00000, 0.90909, 1.00000}, {0.00000, 0.89091, 1.00000}, {0.00000, 0.87273, 1.00000},
    {0.00000, 0.85455, 1.00000}, {0.00000, 0.83636, 1.00000}, {0.00000, 0.81818, 1.00000}, {0.00000, 0.80000, 1.00000},
    {0.00000, 0.78182, 1.00000}, {0.00000, 0.76364, 1.00000}, {0.00000, 0.74545, 1.00000}, {0.00000, 0.72727, 1.00000},
    {0.00000, 0.70909, 1.00000}, {0.00000, 0.69091, 1.00000}, {0.00000, 0.67273, 1.00000}, {0.00000, 0.65455, 1.00000},
    {0.00000, 0.63636, 1.00000}, {0.00000, 0.61818, 1.00000}, {0.00000, 0.60000, 1.00000}, {0.00000, 0.58182, 1.00000},
    {0.00000, 0.56364, 1.00000}, {0.00000, 0.54545, 1.00000}, {0.00000, 0.52727, 1.00000}, {0.00000, 0.50909, 1.00000},
    {0.00000, 0.49091, 1.00000}, {0.00000, 0.47273, 1.00000}, {0.00000, 0.45455, 1.00000}, {0.00000, 0.43636, 1.00000},
    {0.00000, 0.41818, 1.00000}, {0.00000, 0.40000, 1.00000}, {0.00000, 0.38182, 1.00000}, {0.00000, 0.36364, 1.00000},
    {0.00000, 0.34546, 1.00000}, {0.00000, 0.32727, 1.00000}, {0.00000, 0.30909, 1.00000}, {0.00000, 0.29091, 1.00000},
    {0.00000, 0.27273, 1.00000}, {0.00000, 0.25455, 1.00000}, {0.00000, 0.23636, 1.00000}, {0.00000, 0.21818, 1.00000},
    {0.00000, 0.20000, 1.00000}, {0.00000, 0.18182, 1.00000}, {0.00000, 0.16364, 1.00000}, {0.00000, 0.14546, 1.00000},
    {0.00000, 0.12727, 1.00000}, {0.00000, 0.10909, 1.00000}, {0.00000, 0.09091, 1.00000}, {0.00000, 0.07273, 1.00000},
    {0.00000, 0.05455, 1.00000}, {0.00000, 0.03636, 1.00000}, {0.00000, 0.01818, 1.00000}, {0.00000, 0.00000, 1.00000},
    {0.18000, 0.18000, 0.18000}, {0.19491, 0.19491, 0.19491}, {0.20982, 0.20982, 0.20982}, {0.22473, 0.22473, 0.22473},
    {0.23964, 0.23964, 0.23964}, {0.25455, 0.25455, 0.25455}, {0.26945, 0.26945, 0.26945}, {0.28436, 0.28436, 0.28436},
    {0.29927, 0.29927, 0.29927}, {0.31418, 0.31418, 0.31418}, {0.32909, 0.32909, 0.32909}, {0.34400, 0.34400, 0.34400},
    {0.35891, 0.35891, 0.35891}, {0.37382, 0.37382, 0.37382}, {0.38873, 0.38873, 0.38873}, {0.40364, 0.40364, 0.40364},
    {0.41855, 0.41855, 0.41855}, {0.43345, 0.43345, 0.43345}, {0.44836, 0.44836, 0.44836}, {0.46327, 0.46327, 0.46327},
    {0.47818, 0.47818, 0.47818}, {0.49309, 0.49309, 0.49309}, {0.50800, 0.50800, 0.50800}, {0.52291, 0.52291, 0.52291},
    {0.53782, 0.53782, 0.53782}, {0.55273, 0.55273, 0.55273}, {0.56764, 0.56764, 0.56764}, {0.58255, 0.58255, 0.58255},
    {0.59745, 0.59745, 0.59745}, {0.61236, 0.61236, 0.61236}, {0.62727, 0.62727, 0.62727}, {0.64218, 0.64218, 0.64218},
    {0.65709, 0.65709, 0.65709}, {0.67200, 0.67200, 0.67200}, {0.68691, 0.68691, 0.68691}, {0.70182, 0.70182, 0.70182},
    {0.71673, 0.71673, 0.71673}, {0.73164, 0.73164, 0.73164}, {0.74655, 0.74655, 0.74655}, {0.76145, 0.76145, 0.76145},
    {0.77636, 0.77636, 0.77636}, {0.79127, 0.79127, 0.79127}, {0.80618, 0.80618, 0.80618}, {0.82109, 0.82109, 0.82109},
    {0.83600, 0.83600, 0.83600}, {0.85091, 0.85091, 0.85091}, {0.86582, 0.86582, 0.86582}, {0.88073, 0.88073, 0.88073},
    {0.89564, 0.89564, 0.89564}, {0.91055, 0.91055, 0.91055}, {0.92545, 0.92545, 0.92545}, {0.94036, 0.94036, 0.94036},
    {0.95527, 0.95527, 0.95527}, {0.97018, 0.97018, 0.97018}, {0.98509, 0.98509, 0.98509}, {1.00000, 1.00000, 1.00000},
    {0.00000, 0.00000, 0.18000}, {0.00000, 0.00000, 0.20733}, {0.00000, 0.00000, 0.23467}, {0.00000, 0.00000, 0.26200},
    {0.00000, 0.00000, 0.28933}, {0.00000, 0.00000, 0.31667}, {0.00000, 0.00000, 0.34400}, {0.00000, 0.00000, 0.37133},
    {0.00000, 0.00000, 0.39867}, {0.00000, 0.00000, 0.42600}, {0.00000, 0.00000, 0.45333}, {0.00000, 0.00000, 0.48067},
    {0.00000, 0.00000, 0.50800}, {0.00000, 0.00000, 0.53533}, {0.00000, 0.00000, 0.56267}, {0.00000, 0.00000, 0.59000},
    {0.00000, 0.00000, 0.61733}, {0.00000, 0.00000, 0.64467}, {0.00000, 0.00000, 0.67200}, {0.00000, 0.00000, 0.69933},
    {0.00000, 0.00000, 0.72667}, {0.00000, 0.00000, 0.75400}, {0.00000, 0.00000, 0.78133}, {0.00000, 0.00000, 0.80867},
    {0.00000, 0.00000, 0.83600}, {0.00000, 0.00000, 0.86333}, {0.00000, 0.00000, 0.89067}, {0.00000, 0.00000, 0.91800},
    {0.03333, 0.03333, 1.00000}, {0.06667, 0.06667, 1.00000}, {0.10000, 0.10000, 1.00000}, {0.13333, 0.13333, 1.00000},
    {0.16667, 0.16667, 1.00000}, {0.20000, 0.20000, 1.00000}, {0.23333, 0.23333, 1.00000}, {0.26667, 0.26667, 1.00000},
    {0.30000, 0.30000, 1.00000}, {0.33333, 0.33333, 1.00000}, {0.36667, 0.36667, 1.00000}, {0.40000, 0.40000, 1.00000},
    {0.43333, 0.43333, 1.00000}, {0.46667, 0.46667, 1.00000}, {0.50000, 0.50000, 1.00000}, {0.53333, 0.53333, 1.00000},
    {0.56667, 0.56667, 1.00000}, {0.60000, 0.60000, 1.00000}, {0.63333, 0.63333, 1.00000}, {0.66667, 0.66667, 1.00000},
    {0.70000, 0.70000, 1.00000}, {0.73333, 0.73333, 1.00000}, {0.76667, 0.76667, 1.00000}, {0.80000, 0.80000, 1.00000},
    {0.83333, 0.83333, 1.00000}, {0.86667, 0.86667, 1.00000}, {0.90000, 0.90000, 1.00000}, {0.93333, 0.93333, 1.00000},
    {0.18000, 0.00000, 0.18000}, {0.20733, 0.00000, 0.20733}, {0.23467, 0.00000, 0.23467}, {0.26200, 0.00000, 0.26200},
    {0.28933, 0.00000, 0.28933}, {0.31667, 0.00000, 0.31667}, {0.34400, 0.00000, 0.34400}, {0.37133, 0.00000, 0.37133},
    {0.39867, 0.00000, 0.39867}, {0.42600, 0.00000, 0.42600}, {0.45333, 0.00000, 0.45333}, {0.48067, 0.00000, 0.48067},
    {0.50800, 0.00000, 0.50800}, {0.53533, 0.00000, 0.53533}, {0.56267, 0.00000, 0.56267}, {0.59000, 0.00000, 0.59000},
    {0.61733, 0.00000, 0.61733}, {0.64467, 0.00000, 0.64467}, {0.67200, 0.00000, 0.67200}, {0.69933, 0.00000, 0.69933},
    {0.72667, 0.00000, 0.72667}, {0.75400, 0.00000, 0.75400}, {0.78133, 0.00000, 0.78133}, {0.80867, 0.00000, 0.80867},
    {0.83600, 0.00000, 0.83600}, {0.86333, 0.00000, 0.86333}, {0.89067, 0.00000, 0.89067}, {0.91800, 0.00000, 0.91800},
    {1.00000, 0.03333, 1.00000}, {1.00000, 0.06667, 1.00000}, {1.00000, 0.10000, 1.00000}, {1.00000, 0.13333, 1.00000},
    {1.00000, 0.16667, 1.00000}, {1.00000, 0.20000, 1.00000}, {1.00000, 0.23333, 1.00000}, {1.00000, 0.26667, 1.00000},
    {1.00000, 0.30000, 1.00000}, {1.00000, 0.33333, 1.00000}, {1.00000, 0.36667, 1.00000}, {1.00000, 0.40000, 1.00000},
    {1.00000, 0.43333, 1.00000}, {1.00000, 0.46667, 1.00000}, {1.00000, 0.50000, 1.00000}, {1.00000, 0.53333, 1.00000},
    {1.00000, 0.56667, 1.00000}, {1.00000, 0.60000, 1.00000}, {1.00000, 0.63333, 1.00000}, {1.00000, 0.66667, 1.00000},
    {1.00000, 0.70000, 1.00000}, {1.00000, 0.73333, 1.00000}, {1.00000, 0.76667, 1.00000}, {1.00000, 0.80000, 1.00000},
    {1.00000, 0.83333, 1.00000}, {1.00000, 0.86667, 1.00000}, {1.00000, 0.90000, 1.00000}, {1.00000, 0.93333, 1.00000},
    {0.18000, 0.00000, 0.00000}, {0.20733, 0.00000, 0.00000}, {0.23467, 0.00000, 0.00000}, {0.26200, 0.00000, 0.00000},
    {0.28933, 0.00000, 0.00000}, {0.31667, 0.00000, 0.00000}, {0.34400, 0.00000, 0.00000}, {0.37133, 0.00000, 0.00000},
    {0.39867, 0.00000, 0.00000}, {0.42600, 0.00000, 0.00000}, {0.45333, 0.00000, 0.00000}, {0.48067, 0.00000, 0.00000},
    {0.50800, 0.00000, 0.00000}, {0.53533, 0.00000, 0.00000}, {0.56267, 0.00000, 0.00000}, {0.59000, 0.00000, 0.00000},
    {0.61733, 0.00000, 0.00000}, {0.64467, 0.00000, 0.00000}, {0.67200, 0.00000, 0.00000}, {0.69933, 0.00000, 0.00000},
    {0.72667, 0.00000, 0.00000}, {0.75400, 0.00000, 0.00000}, {0.78133, 0.00000, 0.00000}, {0.80867, 0.00000, 0.00000},
    {0.83600, 0.00000, 0.00000}, {0.86333, 0.00000, 0.00000}, {0.89067, 0.00000, 0.00000}, {0.91800, 0.00000, 0.00000},
    {1.00000, 0.03333, 0.03333}, {1.00000, 0.06667, 0.06667}, {1.00000, 0.10000, 0.10000}, {1.00000, 0.13333, 0.13333},
    {1.00000, 0.16667, 0.16667}, {1.00000, 0.20000, 0.20000}, {1.00000, 0.23333, 0.23333}, {1.00000, 0.26667, 0.26667},
    {1.00000, 0.30000, 0.30000}, {1.00000, 0.33333, 0.33333}, {1.00000, 0.36667, 0.36667}, {1.00000, 0.40000, 0.40000},
    {1.00000, 0.43333, 0.43333}, {1.00000, 0.46667, 0.46667}, {1.00000, 0.50000, 0.50000}, {1.00000, 0.53333, 0.53333},
    {1.00000, 0.56667, 0.56667}, {1.00000, 0.60000, 0.60000}, {1.00000, 0.63333, 0.63333}, {1.00000, 0.66667, 0.66667},
    {1.00000, 0.70000, 0.70000}, {1.00000, 0.73333, 0.73333}, {1.00000, 0.76667, 0.76667}, {1.00000, 0.80000, 0.80000},
    {1.00000, 0.83333, 0.83333}, {1.00000, 0.86667, 0.86667}, {1.00000, 0.90000, 0.90000}, {1.00000, 0.93333, 0.93333},
    {0.18000, 0.18000, 0.00000}, {0.20733, 0.20733, 0.00000}, {0.23467, 0.23467, 0.00000}, {0.26200, 0.26200, 0.00000},
    {0.28933, 0.28933, 0.00000}, {0.31667, 0.31667, 0.00000}, {0.34400, 0.34400, 0.00000}, {0.37133, 0.37133, 0.00000},
    {0.39867, 0.39867, 0.00000}, {0.42600, 0.42600, 0.00000}, {0.45333, 0.45333, 0.00000}, {0.48067, 0.48067, 0.00000},
    {0.50800, 0.50800, 0.00000}, {0.53533, 0.53533, 0.00000}, {0.56267, 0.56267, 0.00000}, {0.59000, 0.59000, 0.00000},
    {0.61733, 0.61733, 0.00000}, {0.64467, 0.64467, 0.00000}, {0.67200, 0.67200, 0.00000}, {0.69933, 0.69933, 0.00000},
    {0.72667, 0.72667, 0.00000}, {0.75400, 0.75400, 0.00000}, {0.78133, 0.78133, 0.00000}, {0.80867, 0.80867, 0.00000},
    {0.83600, 0.83600, 0.00000}, {0.86333, 0.86333, 0.00000}, {0.89067, 0.89067, 0.00000}, {0.91800, 0.91800, 0.00000},
    {1.00000, 1.00000, 0.03333}, {1.00000, 1.00000, 0.06667}, {1.00000, 1.00000, 0.10000}, {1.00000, 1.00000, 0.13333},
    {1.00000, 1.00000, 0.16667}, {1.00000, 1.00000, 0.20000}, {1.00000, 1.00000, 0.23333}, {1.00000, 1.00000, 0.26667},
    {1.00000, 1.00000, 0.30000}, {1.00000, 1.00000, 0.33333}, {1.00000, 1.00000, 0.36667}, {1.00000, 1.00000, 0.40000},
    {1.00000, 1.00000, 0.43333}, {1.00000, 1.00000, 0.46667}, {1.00000, 1.00000, 0.50000}, {1.00000, 1.00000, 0.53333},
    {1.00000, 1.00000, 0.56667}, {1.00000, 1.00000, 0.60000}, {1.00000, 1.00000, 0.63333}, {1.00000, 1.00000, 0.66667},
    {1.00000, 1.00000, 0.70000}, {1.00000, 1.00000, 0.73333}, {1.00000, 1.00000, 0.76667}, {1.00000, 1.00000, 0.80000},
    {1.00000, 1.00000, 0.83333}, {1.00000, 1.00000, 0.86667}, {1.00000, 1.00000, 0.90000}, {1.00000, 1.00000, 0.93333},
    {0.00000, 0.18000, 0.00000}, {0.00000, 0.20733, 0.00000}, {0.00000, 0.23467, 0.00000}, {0.00000, 0.26200, 0.00000},
    {0.00000, 0.28933, 0.00000}, {0.00000, 0.31667, 0.00000}, {0.00000, 0.34400, 0.00000}, {0.00000, 0.37133, 0.00000},
    {0.00000, 0.39867, 0.00000}, {0.00000, 0.42600, 0.00000}, {0.00000, 0.45333, 0.00000}, {0.00000, 0.48067, 0.00000},
    {0.00000, 0.50800, 0.00000}, {0.00000, 0.53533, 0.00000}, {0.00000, 0.56267, 0.00000}, {0.00000, 0.59000, 0.00000},
    {0.00000, 0.61733, 0.00000}, {0.00000, 0.64467, 0.00000}, {0.00000, 0.67200, 0.00000}, {0.00000, 0.69933, 0.00000},
    {0.00000, 0.72667, 0.00000}, {0.00000, 0.75400, 0.00000}, {0.00000, 0.78133, 0.00000}, {0.00000, 0.80867, 0.00000},
    {0.00000, 0.83600, 0.00000}, {0.00000, 0.86333, 0.00000}, {0.00000, 0.89067, 0.00000}, {0.00000, 0.91800, 0.00000},
    {0.03333, 1.00000, 0.03333}, {0.06667, 1.00000, 0.06667}, {0.10000, 1.00000, 0.10000}, {0.13333, 1.00000, 0.13333},
    {0.16667, 1.00000, 0.16667}, {0.20000, 1.00000, 0.20000}, {0.23333, 1.00000, 0.23333}, {0.26667, 1.00000, 0.26667},
    {0.30000, 1.00000, 0.30000}, {0.33333, 1.00000, 0.33333}, {0.36667, 1.00000, 0.36667}, {0.40000, 1.00000, 0.40000},
    {0.43333, 1.00000, 0.43333}, {0.46667, 1.00000, 0.46667}, {0.50000, 1.00000, 0.50000}, {0.53333, 1.00000, 0.53333},
    {0.56667, 1.00000, 0.56667}, {0.60000, 1.00000, 0.60000}, {0.63333, 1.00000, 0.63333}, {0.66667, 1.00000, 0.66667},
    {0.70000, 1.00000, 0.70000}, {0.73333, 1.00000, 0.73333}, {0.76667, 1.00000, 0.76667}, {0.80000, 1.00000, 0.80000},
    {0.83333, 1.00000, 0.83333}, {0.86667, 1.00000, 0.86667}, {0.90000, 1.00000, 0.90000}, {0.93333, 1.00000, 0.93333},
    {0.00000, 0.18000, 0.18000}, {0.00000, 0.20733, 0.20733}, {0.00000, 0.23467, 0.23467}, {0.00000, 0.26200, 0.26200},
    {0.00000, 0.28933, 0.28933}, {0.00000, 0.31667, 0.31667}, {0.00000, 0.34400, 0.34400}, {0.00000, 0.37133, 0.37133},
    {0.00000, 0.39867, 0.39867}, {0.00000, 0.42600, 0.42600}, {0.00000, 0.45333, 0.45333}, {0.00000, 0.48067, 0.48067},
    {0.00000, 0.50800, 0.50800}, {0.00000, 0.53533, 0.53533}, {0.00000, 0.56267, 0.56267}, {0.00000, 0.59000, 0.59000},
    {0.00000, 0.61733, 0.61733}, {0.00000, 0.64467, 0.64467}, {0.00000, 0.67200, 0.67200}, {0.00000, 0.69933, 0.69933},
    {0.00000, 0.72667, 0.72667}, {0.00000, 0.75400, 0.75400}, {0.00000, 0.78133, 0.78133}, {0.00000, 0.80867, 0.80867},
    {0.00000, 0.83600, 0.83600}, {0.00000, 0.86333, 0.86333}, {0.00000, 0.89067, 0.89067}, {0.00000, 0.91800, 0.91800},
    {0.03333, 1.00000, 1.00000}, {0.06667, 1.00000, 1.00000}, {0.10000, 1.00000, 1.00000}, {0.13333, 1.00000, 1.00000},
    {0.16667, 1.00000, 1.00000}, {0.20000, 1.00000, 1.00000}, {0.23333, 1.00000, 1.00000}, {0.26667, 1.00000, 1.00000},
    {0.30000, 1.00000, 1.00000}, {0.33333, 1.00000, 1.00000}, {0.36667, 1.00000, 1.00000}, {0.40000, 1.00000, 1.00000},
    {0.43333, 1.00000, 1.00000}, {0.46667, 1.00000, 1.00000}, {0.50000, 1.00000, 1.00000}, {0.53333, 1.00000, 1.00000},
    {0.56667, 1.00000, 1.00000}, {0.60000, 1.00000, 1.00000}, {0.63333, 1.00000, 1.00000}, {0.66667, 1.00000, 1.00000},
    {0.70000, 1.00000, 1.00000}, {0.73333, 1.00000, 1.00000}, {0.76667, 1.00000, 1.00000}, {0.80000, 1.00000, 1.00000},
    {0.83333, 1.00000, 1.00000}, {0.86667, 1.00000, 1.00000}, {0.90000, 1.00000, 1.00000}, {0.93333, 1.00000, 1.00000},
    {1.00000, 0.70196, 0.00000}, {0.50196, 0.24314, 0.45882}, {1.00000, 0.40784, 0.00000}, {0.35156, 0.64063, 0.75391},
    {0.75686, 0.00000, 0.12549}, {0.80784, 0.63529, 0.38431}, {0.50588, 0.43922, 0.40000}, {0.00000, 0.49020, 0.20392},
    {0.96471, 0.46275, 0.55686}, {0.00000, 0.32549, 0.54118}, {1.00000, 0.47843, 0.36078}, {0.32549, 0.21569, 0.47843},
    {1.00000, 0.55686, 0.00000}, {0.70196, 0.15686, 0.31765}, {0.95686, 0.78431, 0.00000}, {0.49804, 0.09412, 0.05098},
    {0.57647, 0.66667, 0.00000}, {0.34902, 0.20000, 0.08235}, {0.94510, 0.22745, 0.07451}, {0.13725, 0.17255, 0.08627}};

static const char *symbol2utf[256] = {
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\xc2\xa0",     "\x21",         "\xe2\x88\x80",
    "\x23",         "\xe2\x88\x83", "\x25",         "\x26",         "\xe2\x88\x8b", "\x28",         "\x29",
    "\xe2\x88\x97", "\x2b",         "\x2c",         "\xe2\x88\x92", "\x2e",         "\x2f",         "\x30",
    "\x31",         "\x32",         "\x33",         "\x34",         "\x35",         "\x36",         "\x37",
    "\x38",         "\x39",         "\x3a",         "\x3b",         "\x3c",         "\x3d",         "\x3e",
    "\x3f",         "\xe2\x89\x85", "\xce\x91",     "\xce\x92",     "\xce\xa7",     "\xe2\x88\x86", "\xce\x95",
    "\xce\xa6",     "\xce\x93",     "\xce\x97",     "\xce\x99",     "\xcf\x91",     "\xce\x9a",     "\xce\x9b",
    "\xce\x9c",     "\xce\x9d",     "\xce\x9f",     "\xce\xa0",     "\xce\x98",     "\xce\xa1",     "\xce\xa3",
    "\xce\xa4",     "\xce\xa5",     "\xcf\x82",     "\xe2\x84\xa6", "\xce\x9e",     "\xce\xa8",     "\xce\x96",
    "\x5b",         "\xe2\x88\xb4", "\x5d",         "\xe2\x8a\xa5", "\x5f",         "\xef\xa3\xa5", "\xce\xb1",
    "\xce\xb2",     "\xcf\x87",     "\xce\xb4",     "\xce\xb5",     "\xcf\x86",     "\xce\xb3",     "\xce\xb7",
    "\xce\xb9",     "\xcf\x95",     "\xce\xba",     "\xce\xbb",     "\xce\xbc",     "\xce\xbd",     "\xce\xbf",
    "\xcf\x80",     "\xce\xb8",     "\xcf\x81",     "\xcf\x83",     "\xcf\x84",     "\xcf\x85",     "\xcf\x96",
    "\xcf\x89",     "\xce\xbe",     "\xcf\x88",     "\xce\xb6",     "\x7b",         "\x7c",         "\x7d",
    "\xe2\x88\xbc", "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",
    "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\x3f",         "\xe2\x82\xac",
    "\xcf\x92",     "\xe2\x80\xb2", "\xe2\x89\xa4", "\xe2\x88\x95", "\xe2\x88\x9e", "\xc6\x92",     "\xe2\x99\xa3",
    "\xe2\x99\xa6", "\xe2\x99\xa5", "\xe2\x99\xa0", "\xe2\x86\x94", "\xe2\x86\x90", "\xe2\x86\x91", "\xe2\x86\x92",
    "\xe2\x86\x93", "\xc2\xb0",     "\xc2\xb1",     "\xe2\x80\xb3", "\xe2\x89\xa5", "\xc3\x97",     "\xe2\x88\x9d",
    "\xe2\x88\x82", "\xe2\x80\xa2", "\xc3\xb7",     "\xe2\x89\xa0", "\xe2\x89\xa1", "\xe2\x89\x88", "\xe2\x80\xa6",
    "\xef\xa3\xa6", "\xef\xa3\xa7", "\xe2\x86\xb5", "\xe2\x84\xb5", "\xe2\x84\x91", "\xe2\x84\x9c", "\xe2\x84\x98",
    "\xe2\x8a\x97", "\xe2\x8a\x95", "\xe2\x88\x85", "\xe2\x88\xa9", "\xe2\x88\xaa", "\xe2\x8a\x83", "\xe2\x8a\x87",
    "\xe2\x8a\x84", "\xe2\x8a\x82", "\xe2\x8a\x86", "\xe2\x88\x88", "\xe2\x88\x89", "\xe2\x88\xa0", "\xe2\x88\x87",
    "\xef\x9b\x9a", "\xef\x9b\x99", "\xef\x9b\x9b", "\xe2\x88\x8f", "\xe2\x88\x9a", "\xe2\x8b\x85", "\xc2\xac",
    "\xe2\x88\xa7", "\xe2\x88\xa8", "\xe2\x87\x94", "\xe2\x87\x90", "\xe2\x87\x91", "\xe2\x87\x92", "\xe2\x87\x93",
    "\xe2\x97\x8a", "\xe2\x8c\xa9", "\xef\xa3\xa8", "\xef\xa3\xa9", "\xef\xa3\xaa", "\xe2\x88\x91", "\xef\xa3\xab",
    "\xef\xa3\xac", "\xef\xa3\xad", "\xef\xa3\xae", "\xef\xa3\xaf", "\xef\xa3\xb0", "\xef\xa3\xb1", "\xef\xa3\xb2",
    "\xef\xa3\xb3", "\xef\xa3\xb4", "\x3f",         "\xe2\x8c\xaa", "\xe2\x88\xab", "\xe2\x8c\xa0", "\xef\xa3\xb5",
    "\xe2\x8c\xa1", "\xef\xa3\xb6", "\xef\xa3\xb7", "\xef\xa3\xb8", "\xef\xa3\xb9", "\xef\xa3\xba", "\xef\xa3\xbb",
    "\xef\xa3\xbc", "\xef\xa3\xbd", "\xef\xa3\xbe", "\x3f",
};

static double rx = 0, ry = 0, seglen = 0;

static int newseg = 0, idash = 0, dtype = 0;

static int dash_table[35][10] = {
    {8, 4, 2, 4, 2, 4, 2, 4, 6, 0},  {6, 4, 2, 4, 2, 4, 6, 0, 0, 0}, {4, 4, 2, 4, 6, 0, 0, 0, 0, 0},
    {8, 3, 2, 3, 2, 3, 2, 3, 6, 0},  {6, 3, 2, 3, 2, 3, 6, 0, 0, 0}, {4, 3, 2, 3, 6, 0, 0, 0, 0, 0},
    {8, 3, 2, 3, 2, 3, 2, 3, 4, 0},  {6, 3, 2, 3, 2, 3, 4, 0, 0, 0}, {4, 3, 2, 3, 4, 0, 0, 0, 0, 0},
    {2, 1, 1, 0, 0, 0, 0, 0, 0, 0},  {2, 1, 2, 0, 0, 0, 0, 0, 0, 0}, {2, 1, 6, 0, 0, 0, 0, 0, 0, 0},
    {2, 1, 8, 0, 0, 0, 0, 0, 0, 0},  {6, 1, 3, 1, 3, 1, 6, 0, 0, 0}, {4, 1, 3, 1, 6, 0, 0, 0, 0, 0},
    {8, 6, 2, 1, 2, 1, 2, 1, 2, 0},  {6, 6, 2, 1, 2, 1, 2, 0, 0, 0}, {4, 6, 2, 1, 2, 0, 0, 0, 0, 0},
    {4, 9, 3, 5, 3, 0, 0, 0, 0, 0},  {2, 9, 3, 0, 0, 0, 0, 0, 0, 0}, {2, 5, 5, 0, 0, 0, 0, 0, 0, 0},
    {2, 5, 3, 0, 0, 0, 0, 0, 0, 0},  {6, 1, 4, 1, 4, 1, 8, 0, 0, 0}, {4, 1, 4, 1, 8, 0, 0, 0, 0, 0},
    {2, 1, 1, 0, 0, 0, 0, 0, 0, 0},  {2, 8, 1, 0, 0, 0, 0, 0, 0, 0}, {4, 16, 5, 8, 5, 0, 0, 0, 0, 0},
    {2, 16, 5, 0, 0, 0, 0, 0, 0, 0}, {8, 8, 4, 1, 4, 1, 4, 1, 4, 0}, {6, 8, 4, 1, 4, 1, 4, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {2, 8, 5, 0, 0, 0, 0, 0, 0, 0},
    {2, 1, 2, 0, 0, 0, 0, 0, 0, 0},  {4, 8, 4, 1, 4, 0, 0, 0, 0, 0}};

static int dash_list[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static int roman[4] = {3, 12, 16, 11};

static int greek[4] = {4, 7, 10, 7};

static double xfac[4] = {1, -1, 0, 0};

static double yfac[4] = {0, 0, 1, -1};

static char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static double cxl, cxr, cyb, cyt;

static double bx = 1, by = 0, ux = 0, uy = 1;

static double sin_f = 0, cos_f = 1;

static double cur_wn[4], cur_vp[4];

static gks_state_list_t *gkss = NULL;

void gks_init_core(gks_state_list_t *list)
{
  gkss = list;
}

gks_list_t *gks_list_find(gks_list_t *list, int element)
{
  while (list != NULL)
    {
      if (list->item == element) return list;
      list = list->next;
    }
  return NULL;
}

gks_list_t *gks_list_add(gks_list_t *list, int element, void *ptr)
{
  gks_list_t *head, *prev = NULL;

  head = list;
  while (list != NULL)
    {
      prev = list;
      list = list->next;
    }

  list = (gks_list_t *)gks_malloc(sizeof(gks_list_t));

  list->item = element;
  list->ptr = ptr;
  list->next = NULL;
  if (prev != NULL)
    prev->next = list;
  else
    head = list;

  return head;
}

gks_list_t *gks_list_del(gks_list_t *list, int element)
{
  gks_list_t *head, *prev = NULL, *next;

  head = list;
  while (list != NULL)
    {
      if (list->item == element)
        {
          next = list->next;
          if (list->ptr != NULL) gks_free(list->ptr);
          gks_free((void *)list);

          if (prev != NULL)
            prev->next = next;
          else
            head = next;
          break;
        }
      else
        {
          prev = list;
          list = list->next;
        }
    }

  return head;
}

void gks_list_free(gks_list_t *list)
{
  gks_list_t *next;

  while (list != NULL)
    {
      next = list->next;
      if (list->ptr != NULL) gks_free(list->ptr);
      gks_free((void *)list);
      list = next;
    }
}

void gks_inq_pattern_array(int index, int *pa)
{
  int i, j, k;

  j = index;
  if (j > 119)
    j = 119;
  else if (j < 0)
    j = 0;

  k = pattern[j][0];
  for (i = 0; i <= k; i++) pa[i] = pattern[j][i];
}

void gks_set_pattern_array(int index, int *pa)
{
  int i, j, k;

  j = index;
  if (j >= 0 && j <= 119)
    {
      k = pa[0];
      if (k == 4 || k == 8 || k == 32)
        for (i = 0; i <= k; i++) pattern[j][i] = pa[i];
    }
}

void gks_inq_rgb(int index, double *red, double *green, double *blue)
{
  if (index >= 0 && index < MAX_COLOR)
    {
      *red = rgb[index][0];
      *green = rgb[index][1];
      *blue = rgb[index][2];
    }
}

void gks_set_rgb(int index, double red, double green, double blue)
{
  if (index >= 0 && index < MAX_COLOR)
    {
      rgb[index][0] = red;
      rgb[index][1] = green;
      rgb[index][2] = blue;
    }
}

void gks_fit_ws_viewport(double *viewport, double xmax, double ymax, double margin)
{
  int i;
  double w, h, ratio, half_margin;

  /* fit viewport into available device space */

  ratio = (viewport[1] - viewport[0]) / (viewport[3] - viewport[2]);
  half_margin = margin / 2.0;
  for (i = 1; i <= 2; i++)
    {
      w = viewport[1] - viewport[0];
      if (w > xmax - margin)
        {
          viewport[0] = half_margin;
          viewport[1] = xmax - half_margin;
          viewport[3] = viewport[2] + (xmax - margin) / ratio;
        }
      h = viewport[3] - viewport[2];
      if (h > ymax - margin)
        {
          viewport[1] = viewport[0] + (ymax - margin) * ratio;
          viewport[2] = half_margin;
          viewport[3] = ymax - half_margin;
        }
    }
  if (viewport[1] > xmax)
    {
      viewport[0] = xmax - (viewport[1] - viewport[0]);
      viewport[1] = xmax;
    }
  if (viewport[3] > ymax)
    {
      viewport[2] = ymax - (viewport[3] - viewport[2]);
      viewport[3] = ymax;
    }
}

void gks_set_norm_xform(int tnr, double *window, double *viewport)
{
  gkss->a[tnr] = (viewport[1] - viewport[0]) / (window[1] - window[0]);
  gkss->b[tnr] = viewport[0] - window[0] * gkss->a[tnr];
  gkss->c[tnr] = (viewport[3] - viewport[2]) / (window[3] - window[2]);
  gkss->d[tnr] = viewport[2] - window[2] * gkss->c[tnr];
}

void gks_WC_to_NDC(int tnr, double *x, double *y)
{
  WC_to_NDC(*x, *y, tnr, *x, *y);
}

void gks_seg_xform(double *x, double *y)
{
  double xx;

  xx = *x * gkss->mat[0][0] + *y * gkss->mat[0][1] + gkss->mat[2][0];
  *y = *x * gkss->mat[1][0] + *y * gkss->mat[1][1] + gkss->mat[2][1];
  *x = xx;
}

static void gks_seg_xform_rel(double *x, double *y)
{
  double xx;

  xx = *x * gkss->mat[0][0] + *y * gkss->mat[0][1];
  *y = *x * gkss->mat[1][0] + *y * gkss->mat[1][1];
  *x = xx;
}

void gks_set_dev_xform(gks_state_list_t *s, double *window, double *viewport)
{
  int i;
  int tnr;

  for (i = 0; i < 4; i++)
    {
      cur_wn[i] = window[i];
      cur_vp[i] = viewport[i];
    }
  tnr = (s->clip == GKS_K_CLIP) ? s->cntnr : 0;

  if (s->clip == GKS_K_CLIP)
    {
      cxl = MAX(s->viewport[tnr][0], window[0]);
      cxr = MIN(s->viewport[tnr][1], window[1]);
      cyb = MAX(s->viewport[tnr][2], window[2]);
      cyt = MIN(s->viewport[tnr][3], window[3]);
    }
  else
    {
      cxl = window[0];
      cxr = window[1];
      cyb = window[2];
      cyt = window[3];
    }

  cxl -= FEPS;
  cxr += FEPS;
  cyb -= FEPS;
  cyt += FEPS;
}

void gks_inq_dev_xform(double *window, double *viewport)
{
  int i;

  for (i = 0; i < 4; i++)
    {
      window[i] = cur_wn[i];
      viewport[i] = cur_vp[i];
    }
}

void gks_set_chr_xform(void)
{
  int tnr;
  double chux, chuy, scale, chh, chxp, slant;
  double rad;

  tnr = gkss->cntnr;

  chux = gkss->chup[0];
  chuy = gkss->chup[1];
  chh = gkss->chh;
  chxp = gkss->chxp;
  slant = gkss->txslant;

  /* scale to normalize the up vector */
  scale = sqrt(chux * chux + chuy * chuy);
  chux /= scale;
  chuy /= scale;

  /* compute character up vector */
  ux = chux * chh;
  uy = chuy * chh;

  /* normalize character up vector */
  WC_to_NDC_rel(ux, uy, tnr, ux, uy);

  /* compute character base vector (right angle to up vector) */
  bx = chuy * chh;
  by = -chux * chh;

  /* normalize character base vector */
  WC_to_NDC_rel(bx, by, tnr, bx, by);

  bx *= chxp;
  by *= chxp;

  rad = -slant / 180.0 * M_PI;
  cos_f = cos(rad);
  sin_f = sin(rad);
}

static void gks_chr_xform(double *x, double *y, int size)
{
  double xn, yn;

  xn = *x / size;
  yn = *y / size;

  xn = cos_f * xn - sin_f * yn;
  yn = cos_f * yn;

  *x = bx * xn + ux * yn;
  *y = by * xn + uy * yn;
}

void gks_chr_height(double *height)
{
  double w, h;

  w = 0;
  h = sqrt(ux * ux + uy * uy);
  gks_seg_xform_rel(&w, &h);

  *height = sqrt(w * w + h * h);
}

void gks_get_dash(int ltype, double scale, char *dash)
{
  int i, len;
  char buf[20];
  double value;

  len = dash_table[ltype + 30][0];
  strcpy(dash, "[");
  for (i = 1; i <= len; i++)
    {
      value = 0.1 * floor(dash_table[ltype + 30][i] * scale * 10 + 0.5);
      snprintf(buf, 20, "%g%s", value, i < len ? " " : "");
      strcat(dash, buf);
    }
  strcat(dash, "]");
}

void gks_get_dash_list(int ltype, double scale, int list[10])
{
  int i, len;
  double value;

  if (scale < 1) scale = 1;

  len = dash_table[ltype + 30][0];
  list[0] = len;
  for (i = 1; i <= len; i++)
    {
      value = dash_table[ltype + 30][i] * scale;
      list[i] = (int)(value + 0.5);
    }
}

void gks_move(double x, double y, void (*move)(double, double))
{
  rx = x;
  ry = y;

  (*move)(x, y);
}

void gks_dash(double x, double y, void (*move)(double, double), void (*draw)(double, double))
{
  double diag, dash, dist;
  int dark, itempa, len;
  double dx, dy, xd, yd;

  if (dtype == 1 || dtype == 0)
    {
      /* draw a solid line */
      (*draw)(x, y);
    }
  else
    {
      /* draw a dashed line */
      len = dash_list[0];

      /* initialize variables */
      xd = rx;
      yd = ry;
      dx = x - xd;
      dy = y - yd;
      dist = sqrt(dx * dx + dy * dy);
      if (dist > 0)
        {
          diag = dist;
          if (newseg) goto L2;

          idash = len;
        /* loop to output line */
        L1:
          idash = idash % len + 1;
        L2:
          itempa = idash;
          dash = dash_list[itempa] * 0.002;
          if (fabs(seglen) <= FEPS) seglen = dash;

          xd += dx * seglen / diag;
          yd += dy * seglen / diag;
          dark = itempa % 2 == 1;
          newseg = seglen < dist;

          /* be sure that line segment will not overshoot point */
          if (newseg)
            {
              rx = xd;
              ry = yd;
              /* see if line is a dark vector */
              if (dark)
                (*draw)(xd, yd);
              else
                (*move)(xd, yd);

              /* calculate remaining distance to point */
              dist -= seglen;
              seglen = 0;
              goto L1;
            }
          else
            {
              /* output remainder of line */
              rx = x;
              ry = y;

              /* calculate segment fragment unused */
              seglen -= dist;
              if (dark || fabs(seglen) <= FEPS) (*draw)(x, y);
            }
        }
    }
}

static int clip_code(double x, double y)
{
  int code = 0;

  if (x < cxl)
    code = LEFT;
  else if (x > cxr)
    code = RIGHT;

  if (y < cyb)
    code |= BOTTOM;
  else if (y > cyt)
    code |= TOP;

  return code;
}

static int clip_line(double *x0, double *y0, double *x1, double *y1)
{
  int c, c0, c1;
  double x = 0, y = 0;

  c0 = clip_code(*x0, *y0);
  c1 = clip_code(*x1, *y1);

  while (c0 | c1)
    {
      if (c0 & c1) return 0;
      c = c0 ? c0 : c1;

      if (c & LEFT)
        {
          x = cxl;
          y = *y0 + (*y1 - *y0) * (cxl - *x0) / (*x1 - *x0);
        }
      else if (c & RIGHT)
        {
          x = cxr;
          y = *y0 + (*y1 - *y0) * (cxr - *x0) / (*x1 - *x0);
        }
      else if (c & BOTTOM)
        {
          x = *x0 + (*x1 - *x0) * (cyb - *y0) / (*y1 - *y0);
          y = cyb;
        }
      else if (c & TOP)
        {
          x = *x0 + (*x1 - *x0) * (cyt - *y0) / (*y1 - *y0);
          y = cyt;
        }

      if (c == c0)
        {
          *x0 = x;
          *y0 = y;
          c0 = clip_code(x, y);
        }
      else
        {
          *x1 = x;
          *y1 = y;
          c1 = clip_code(x, y);
        }
    }
  return 1;
}

void gks_emul_polyline(int n, double *px, double *py, int ltype, int tnr, void (*move)(double x, double y),
                       void (*draw)(double x, double y))
{
  double x0, y0, x, y, x1, y1;
  int clip = 1, visible;
  int i, j, m;

  dtype = ltype;
  seglen = 0;
  newseg = 1;
  idash = 0;

  gks_get_dash_list(ltype, gkss->lwidth, dash_list);

  WC_to_NDC(px[0], py[0], tnr, x0, y0);
  gks_seg_xform(&x0, &y0);

  m = ltype == 0 ? n + 1 : n;

  for (i = 1; i < m; i++)
    {
      j = i < n ? i : 0;

      WC_to_NDC(px[j], py[j], tnr, x1, y1);
      gks_seg_xform(&x1, &y1);

      x = x1;
      y = y1;
      visible = clip_line(&x0, &y0, &x1, &y1);

      if (visible)
        {
          if (clip)
            {
              move(x0, y0);
              clip = 0;
            }
          draw(x1, y1);
        }

      if (x != x1 || y != y1 || !visible) clip = 1;
      x0 = x;
      y0 = y;
    }
}

void gks_emul_polymarker(int n, double *px, double *py, void (*marker)(double x, double y, int mtype))
{
  int i;
  int tnr, mtype;
  double xn, yn;

  tnr = gkss->cntnr;
  mtype = gkss->mtype;

  for (i = 0; i < n; i++)
    {
      WC_to_NDC(px[i], py[i], tnr, xn, yn);
      gks_seg_xform(&xn, &yn);

      if (cxl <= xn && xn <= cxr && cyb <= yn && yn <= cyt) marker(xn, yn, mtype);
    }
}

static int map_font(int font)
{
  /* map hardware text font to stroke font */
  int family, type;

  font = abs(font);
  family = (font - 1) % 8 + 1;
  type = (font - 1) / 8;
  if (type > 3) type = 3;

  if (family != 7)
    font = roman[type];
  else
    font = greek[type];

  return font;
}

static void inq_text_extent(char *chars, int nchars, int font, int prec, int *txx, int *size, int *bottom, int *base,
                            int *cap, int *top)
{
  int i;
  stroke_data_t s;

  *txx = 0;
  if (nchars > 0)
    {
      if (prec == GKS_K_TEXT_PRECISION_STROKE)
        {
          for (i = 0; i < nchars; i++)
            {
              gks_lookup_font(gkss->fontfile, gkss->version, font, chars[i], &s);
              if (chars[i] != ' ')
                *txx += s.right - s.left;
              else
                *txx += s.size / 2;
            }
        }
      else
        {
          for (i = 0; i < nchars; i++)
            {
              gks_lookup_afm(font, chars[i], &s);
              *txx += s.right - s.left;
            }
        }
    }
  else
    {
      if (prec == GKS_K_TEXT_PRECISION_STROKE)
        gks_lookup_font(gkss->fontfile, gkss->version, font, ' ', &s);
      else
        gks_lookup_afm(font, ' ', &s);
    }

  *size = s.size;
  *bottom = s.bottom;
  *base = s.base;
  *cap = s.cap;
  *top = s.top;
}

static void draw_character(double x, double y, char chr, int font,
                           void (*polyline)(int n, double *px, double *py, int ltype, int tnr),
                           void (*fillarea)(int n, double *px, double *py, int tnr), int flag)
{
  int i;
  stroke_data_t s;
  int xmin, xmax, ymin, ymax, xc, yc, n;
  double xn, yn, px[64], py[64];
  double window[4], viewport[4], mszsc;
  double scalex = 0, scaley = 0, center = 0, half = 0;

  gks_lookup_font(gkss->fontfile, gkss->version, font, chr, &s);

  if (flag)
    {
      xmin = 127;
      xmax = 0;
      ymin = 127;
      ymax = 0;

      for (i = 0; i < s.length; i++)
        {
          xc = s.coord[i][0];
          if (xc > 127)
            xc = xc - 256;
          else if (xc < 0)
            xc = -xc;
          yc = s.coord[i][1];
          if (xc < xmin)
            xmin = xc;
          else if (xc > xmax)
            xmax = xc;
          if (yc < ymin)
            ymin = yc;
          else if (yc > ymax)
            ymax = yc;
        }
      if (xmax <= xmin)
        {
          xmin = s.left;
          xmax = s.right;
        }
      if (ymax <= ymin)
        {
          ymin = s.base;
          ymax = s.cap;
        }

      mszsc = gkss->mszsc;
      scalex = 0.001 * mszsc / (xmax - xmin);
      scaley = 0.001 * mszsc / (ymax - ymin);

      gks_inq_dev_xform(window, viewport);
      scalex *= (window[1] - window[0]) / (viewport[1] - viewport[0]);
      scaley *= (window[3] - window[2]) / (viewport[3] - viewport[2]);

      center = 0.5 * (xmin + xmax);
      half = 0.5 * (ymin + ymax);
    }

  n = 0;
  for (i = 0; i < s.length; i++)
    {
      xc = s.coord[i][0];
      if (xc > 127) xc = xc - 256;
      yc = s.coord[i][1];

      if (xc < 0)
        {
          if (n > 1)
            {
              if (font == -51)
                if (n > 2) fillarea(n, px, py, 0);

              polyline(n, px, py, GKS_K_LINETYPE_SOLID, 0);
              n = 0;
            }
          xc = -xc;
        }

      if (flag)
        {
          xn = scalex * (xc - center);
          yn = scaley * (yc - half);
        }
      else
        {
          if (s.left == s.right) xc += s.size / 2;
          xn = xc - s.left;
          yn = yc - s.base;
          gks_chr_xform(&xn, &yn, s.size);
        }

      px[n] = x + xn;
      py[n] = y + yn;
      n++;
    }

  if (n > 1)
    {
      if (font == -51)
        if (n > 2) fillarea(n, px, py, 0);

      polyline(n, px, py, GKS_K_LINETYPE_SOLID, 0);
    }
}

void gks_emul_text(double px, double py, int nchars, char *chars,
                   void (*polyline)(int n, double *px, double *py, int ltype, int tnr),
                   void (*fillarea)(int n, double *px, double *py, int tnr))
{
  int i;
  int tnr, font, prec, alh, alv, path;
  double xn, yn, chsp, ax, ay, spacex, spacey;
  int txx, size, bottom, base, cap, top, space;


  char *latin1_str = gks_malloc(nchars + 1);
  gks_utf82latin1(chars, latin1_str);
  chars = latin1_str;
  nchars = strlen(chars);

  tnr = gkss->cntnr;
  WC_to_NDC(px, py, tnr, xn, yn);

  font = gkss->txfont;
  prec = gkss->txprec;
  if (prec != GKS_K_TEXT_PRECISION_STROKE) font = map_font(font);

  gks_set_chr_xform();

  inq_text_extent(chars, nchars, font, prec, &txx, &size, &bottom, &base, &cap, &top);

  /* apply character spacing */
  chsp = gkss->chsp;
  space = nint(chsp * size);
  txx += nchars * space;

  alh = gkss->txal[0];
  alv = gkss->txal[1];
  path = gkss->txp;

  if (path == GKS_K_TEXT_PATH_UP || path == GKS_K_TEXT_PATH_DOWN) txx = size;

  switch (alh)
    {
    case GKS_K_TEXT_HALIGN_CENTER:
      ax = -0.5 * txx;
      break;
    case GKS_K_TEXT_HALIGN_RIGHT:
      ax = -txx;
      break;
    default:
      ax = 0;
    }

  if (path == GKS_K_TEXT_PATH_LEFT)
    {
      inq_text_extent(chars, 1, font, prec, &txx, &size, &bottom, &base, &cap, &top);
      ax = -ax - txx;
    }

  switch (alv)
    {
    case GKS_K_TEXT_VALIGN_TOP:
      ay = base - top;
      break;
    case GKS_K_TEXT_VALIGN_CAP:
      ay = base - cap;
      break;
    case GKS_K_TEXT_VALIGN_HALF:
      ay = (base - cap) * 0.5;
      break;
    case GKS_K_TEXT_VALIGN_BOTTOM:
      ay = base - bottom;
      break;
    default:
      ay = 0;
    }

  gks_chr_xform(&ax, &ay, size);

  xn += ax;
  yn += ay;

  for (i = 0; i < nchars; i++)
    {
      inq_text_extent(chars + i, 1, font, prec, &txx, &size, &bottom, &base, &cap, &top);

      spacex = (txx + space) * xfac[path];
      spacey = (top - bottom + space) * yfac[path];
      gks_chr_xform(&spacex, &spacey, size);

      draw_character(xn, yn, chars[i], font, polyline, fillarea, 0);

      xn += spacex;
      yn += spacey;
    }

  gks_free(latin1_str);
}

static void minmax(int n, double *a, double *amin, double *amax)
{
  int i;

  *amin = *amax = a[0];
  for (i = 1; i < n; i++)
    {
      if (a[i] < *amin)
        *amin = a[i];
      else if (a[i] > *amax)
        *amax = a[i];
    }
}

static void calc_intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4,
                           double *xi, double *yi, int *flag)
{
  int inf1, inf2;
  double a1 = 0, a2 = 0;

  inf1 = fabs(x1 - x2) <= FEPS;
  if (!inf1) a1 = (y2 - y1) / (x2 - x1);

  inf2 = fabs(x3 - x4) <= FEPS;
  if (!inf2) a2 = (y4 - y3) / (x4 - x3);

  if (inf1)
    {
      if (inf2)
        *flag = 0;
      else
        {
          *xi = x1;
          *yi = y3 + a2 * (*xi - x3);
          *flag = 1;
        }
    }
  else
    {
      if (inf2)
        {
          *xi = x3;
          *yi = y1 + a1 * (*xi - x1);
          *flag = 1;
        }
      else
        {
          *flag = fabs(a1 - a2) > FEPS;
          if (*flag)
            {
              *xi = (y1 - y3 - a1 * x1 + a2 * x3) / (a2 - a1);
              *yi = y1 + a1 * (*xi - x1);
            }
        }
    }
}

static void sort(int n, double *x, double *y, int inc)
{
  int i, j;
  int flag;
  double tmp;

  for (i = 0; i < n; i++)
    for (j = i + 1; j < n; j++)
      {
        flag = inc == 0 ? x[j] < x[i] : x[j] > x[i];
        if (flag)
          {
            tmp = x[i];
            x[i] = x[j];
            x[j] = tmp;
            tmp = y[i];
            y[i] = y[j];
            y[j] = tmp;
          }
      }
}

static void fill(int n, double *px, double *py, int tnr, double x0, double xinc, double dx, double xend, double y0,
                 double yinc, double dy, double yend,
                 void (*polyline)(int n, double *px, double *py, int ltype, int tnr))
{
  double x1, x2, x3, x4, xi, epsx;
  double y1, y2, y3, y4, yi, epsy;
  int i, im1, l, ni, flag, inc;
  double sx[128], sy[128];

  xi = yi = 0.0;
  epsx = fabs((xend - x0) * 1.0E-5);
  epsy = fabs((yend - y0) * 1.0E-5);

  l = 0;
  for (;;)
    {
      l++;
      x1 = x0 + l * xinc;
      y1 = y0 + l * yinc;
      if (x1 > xend || y1 > yend) return;
      x2 = x1 + dx;
      y2 = y1 + dy;

      /* calculate all intersections of the line given by the points
       * (x1, y1), (x2, y2) with the polygon's vertices */
      ni = 0;
      for (i = 0; i < n; i++)
        {
          im1 = i == 0 ? n - 1 : i - 1;

          x3 = px[im1];
          y3 = py[im1];
          WC_to_NDC(x3, y3, tnr, x3, y3);
          x4 = px[i];
          y4 = py[i];
          WC_to_NDC(x4, y4, tnr, x4, y4);

          calc_intersect(x1, y1, x2, y2, x3, y3, x4, y4, &xi, &yi, &flag);
          if (flag)
            {
              double xmin, xmax, ymin, ymax;

              xmin = MIN(x3, x4);
              xmax = MAX(x3, x4);
              ymin = MIN(y3, y4);
              ymax = MAX(y3, y4);

              /* be sure that point not outside the polygon */
              if (xi >= xmin - epsx && xi <= xmax + epsx && yi >= ymin - epsy && yi <= ymax + epsy)
                {
                  sx[ni] = xi;
                  sy[ni] = yi;
                  ni++;
                }
            }
        }

      if (ni > 0)
        {
          /* set sort mode */
          inc = l % 2;
          if (fabs(xinc) <= FEPS) /* sort on X coordinates */
            sort(ni, sx, sy, inc);
          else
            /* sort on Y coordinates */
            sort(ni, sy, sx, inc);

          for (i = 0; i < ni - 1; i += 2) polyline(2, sx + i, sy + i, GKS_K_LINETYPE_SOLID, 0);
        }
    }
}

void gks_emul_fillarea(int n, double *px, double *py, int tnr,
                       void (*polyline)(int n, double *px, double *py, int ltype, int tnr), double yres)
{
  int ints, styli;
  double xmin, xmax, ymin, ymax, inc, r;
  int hatch;

  ints = gkss->ints;
  styli = gkss->styli;

  minmax(n, px, &xmin, &xmax);
  minmax(n, py, &ymin, &ymax);
  WC_to_NDC(xmin, xmax, tnr, xmin, xmax);
  WC_to_NDC(ymin, ymax, tnr, ymin, ymax);

  switch (ints)
    {
    case GKS_K_INTSTYLE_HOLLOW:
      polyline(n, px, py, 0, tnr);
      break;

    case GKS_K_INTSTYLE_SOLID:
      fill(n, px, py, tnr, xmin, 0.0, xmax - xmin, xmax, ymin, yres, 0.0, ymax, polyline);
      break;

    case GKS_K_INTSTYLE_HATCH:
      hatch = (styli - 1) % 6 + 1;
      inc = styli > 6 ? 0.02 : 0.01;

      if (hatch == 1 || hatch == 5)
        {
          /* vertical lines */
          fill(n, px, py, tnr, xmin, inc, 0.0, xmax, ymin, 0.0, ymax - ymin, ymax, polyline);
        }
      if (hatch == 2 || hatch == 5)
        {
          /* horizintal lines */
          fill(n, px, py, tnr, xmin, 0.0, xmax - xmin, xmax, ymin, inc, 0.0, ymax, polyline);
        }
      if (hatch == 3 || hatch == 6)
        {
          /* 45 degree lines */
          r = MAX(xmax - xmin, ymax - ymin);
          fill(n, px, py, tnr, xmin, 0.0, r, xmax, ymin - r, inc * sqrt(2.0), r, ymax, polyline);
        }
      if (hatch == 4 || hatch == 6)
        {
          /* -45 degree lines */
          r = MAX(xmax - xmin, ymax - ymin);
          fill(n, px, py, tnr, xmax, 0.0, -r, xmax, ymin - r, inc * sqrt(2.0), r, ymax, polyline);
        }
      break;

    case GKS_K_INTSTYLE_PATTERN:
      /* pattern emulation not yet implemented - draw the border */
      polyline(n, px, py, 0, tnr);
      break;
    }
}

static int have_gksqt(void)
{
  const char *env;
  char *path;
  int result;

  env = gks_getenv("GKS_QT");
  if (env == NULL)
    {
      env = gks_getenv("GRDIR");
      if (env == NULL) env = GRDIR;

      path = (char *)gks_malloc(MAXPATHLEN);
#ifndef _WIN32
#ifdef __APPLE__
      snprintf(path, MAXPATHLEN, "%s/Applications/gksqt.app/Contents/MacOS/gksqt", env);
#else
      snprintf(path, MAXPATHLEN, "%s/bin/gksqt", env);
#endif
#else
      snprintf(path, MAXPATHLEN, "%s\\bin\\gksqt.exe", env);
#endif
    }
  else
    {
      path = (char *)env;
      if (strstr(path, "PATH=") != NULL)
        {
          /* In Julia BinaryBuilder environments, 'path' is a command string containing
             loader path specifications, e.g. 'env LD_LIBRARY_PATH=...' or 'set PATH=...'.
             So we trust that the given command is correct. */
          return 1;
        }
    }

#ifdef _WIN32
  result = _access(path, R_OK);
#else
  result = access(path, R_OK);
#endif

  if (path != env) gks_free(path);

  return result != -1;
}

#ifndef _WIN32

static struct termios saved_term;

static void makeraw(void)
{
  struct termios term;

  term = saved_term;

  term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
  term.c_oflag &= ~OPOST;
  term.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  term.c_cflag &= ~(CSIZE | PARENB);
  term.c_cflag |= CS8;
  term.c_cc[VMIN] = 0;
  term.c_cc[VTIME] = 2; /* 200ms */

  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) < 0)
    {
      perror("tcsetattr");
    }
}

static enum tmux_state_t have_tmux(void)
{
  const char *term_env_var = gks_getenv("TERM");
  if (term_env_var == NULL)
    {
      return NO_TMUX;
    }

  if (strncmp(term_env_var, "screen", 6) == 0 || strncmp(term_env_var, "tmux", 4) == 0)
    {
      /* Check if the tmux session is running locally, otherwise the server cannot be queried */
      if (gks_getenv("TMUX") != NULL)
        {
          FILE *fp;
          char client_termname[80];

          /* Inside tmux we can query the tmux server for the outer terminal name */
          fp = popen("tmux display -p '#{client_termname}'", "r");
          if (fp == NULL)
            {
              /* Reading failed, assume a single tmux session */
              return SINGLE_TMUX_SESSION;
            }
          /* Read the output a line at a time - output it. */
          if (fgets(client_termname, sizeof(client_termname), fp) == NULL)
            {
              /* Reading failed, assume a single tmux session */
              return SINGLE_TMUX_SESSION;
            }
          pclose(fp);
          return (strncmp(client_termname, "screen", 6) == 0 || strncmp(client_termname, "tmux", 4) == 0)
                     ? NESTED_TMUX_SESSION
                     : SINGLE_TMUX_SESSION;
        }
      return SINGLE_TMUX_SESSION;
    }

  return NO_TMUX;
}

static int have_iterm(void)
{
  char *req;
  int cc, len = 0;
  char s[81];

  switch (have_tmux())
    {
    case NESTED_TMUX_SESSION:
      req = "\033Ptmux;\033\033Ptmux;\033\033\033\033]1337;ReportCellSize\a\033\033\\\033\\";
      break;
    case SINGLE_TMUX_SESSION:
      req = "\033Ptmux;\033\033]1337;ReportCellSize\a\033\\";
      break;
    default:
      req = "\033]1337;ReportCellSize\a";
      break;
    }

  if (isatty(STDIN_FILENO))
    {
      tcgetattr(STDIN_FILENO, &saved_term);
      makeraw();

      write(STDOUT_FILENO, req, strlen(req));
      fflush(stdout);

      while ((cc = read(STDIN_FILENO, s + len, 1)) == 1 && len < 80)
        {
          if (s[len++] == '\\') break;
        }
      s[len] = '\0';

      tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term);

      return strstr(s, "1337;ReportCellSize=") != NULL ? 1 : 0;
    }

  return 0;
}

#endif

static int get_default_ws_type(void)
{
  static int default_wstype = 0;

  if (default_wstype == 0)
    {
#ifdef __EMSCRIPTEN__
      default_wstype = 100;
#else
#ifndef _WIN32
#ifdef __APPLE__
      if (gks_getenv("TERM_PROGRAM") != NULL || gks_getenv("TERMINAL_EMULATOR") != NULL)
        default_wstype = have_gksqt() ? 411 : 400;
      else
#else
      if (gks_getenv("DISPLAY") != NULL)
        default_wstype = have_gksqt() ? 411 : 211;
      else
#endif
        {
          if (have_iterm()) default_wstype = 151;
#ifdef __APPLE__
          else if (access("/dev/console", R_OK) == 0)
            default_wstype = have_gksqt() ? 411 : 400;
#endif
          else
            {
              default_wstype = 100;
              gks_perror("cannot open display - headless operation mode active");
            }
        }
#else
      default_wstype = have_gksqt() ? 411 : 41;
#endif
#endif
    }
  return default_wstype;
}

int gks_get_ws_type(void)
{
  const char *env;
  int wstype = 0, i;

  env = gks_getenv("GKS_WSTYPE");
#ifdef VMS
  if (!env) env = gks_getenv("GKS$WSTYPE");
#endif
  if (!env) env = gks_getenv("GKSwstype");

  if (env)
    {
      if (isalpha(*env))
        {
          for (i = 0; i < num_wstypes; i++)
            if (strcmp(wstypes[i].name, env) == 0)
              {
                wstype = wstypes[i].wstype;
                break;
              }
        }
      else if (*env)
        wstype = atoi(env);
      else
        return get_default_ws_type();

      if (!strcmp(env, "bmp") && gks_getenv("GKS_USE_GS_BMP") != NULL)
        {
          wstype = 320;
        }

      if ((!strcmp(env, "jpg") || !strcmp(env, "jpeg")))
        {
          if (gks_getenv("GKS_USE_GS_JPG") != NULL)
            {
              wstype = 321;
            }
          else if (gks_getenv("GKS_USE_AGG_JPG") != NULL)
            {
              wstype = 172;
            }
        }

      if (!strcmp(env, "png"))
        {
          if (gks_getenv("GKS_USE_GS_PNG") != NULL)
            {
              wstype = 322;
            }
          else if (gks_getenv("GKS_USE_AGG_PNG") != NULL)
            {
              wstype = 171;
            }
        }

      if ((!strcmp(env, "tif") || !strcmp(env, "tiff")) && gks_getenv("GKS_USE_GS_TIF") != NULL)
        {
          wstype = 323;
        }

      if (wstype == 0)
        {
          gks_perror("invalid workstation type (%s)", env);
        }
    }

  if (wstype == 0) wstype = get_default_ws_type();

  if (wstype == 411 && getenv("GKS_QT_USE_CAIRO") && *getenv("GKS_QT_USE_CAIRO"))
    {
      wstype = 412;
    }
  else if (wstype == 411 && getenv("GKS_QT_USE_AGG") && *getenv("GKS_QT_USE_AGG"))
    {
      wstype = 413;
    }

  return wstype;
}

void gks_util_inq_text_extent(double px, double py, char *chars, int nchars, double *cpx, double *cpy, double tx[4],
                              double ty[4])
{
  int i;
  int tnr, font, prec, alh, alv, path;
  double x0, y0, xn, yn, chsp, ax, ay, aspace, spacex, spacey;
  int txx, size, bottom, base, cap, top, space;


  char *latin1_str = gks_malloc(nchars + 1);
  gks_utf82latin1(chars, latin1_str);
  chars = latin1_str;
  nchars = strlen(chars);

  tnr = gkss->cntnr;
  WC_to_NDC(px, py, tnr, x0, y0);

  font = gkss->txfont;
  prec = gkss->txprec;

  inq_text_extent(chars, nchars, font, prec, &txx, &size, &bottom, &base, &cap, &top);

  /* apply character spacing */
  chsp = gkss->chsp;
  space = nint(chsp * size);
  txx += nchars * space;

  alh = gkss->txal[0];
  alv = gkss->txal[1];
  path = gkss->txp;

  if (path == GKS_K_TEXT_PATH_UP || path == GKS_K_TEXT_PATH_DOWN) txx = size;

  switch (alh)
    {
    case GKS_K_TEXT_HALIGN_CENTER:
      ax = -0.5 * txx;
      break;
    case GKS_K_TEXT_HALIGN_RIGHT:
      ax = -txx;
      break;
    default:
      ax = 0;
    }

  if (path == GKS_K_TEXT_PATH_LEFT)
    {
      inq_text_extent(chars, 1, font, prec, &txx, &size, &bottom, &base, &cap, &top);
      ax = -ax - txx;
    }

  switch (alv)
    {
    case GKS_K_TEXT_VALIGN_TOP:
      ay = base - top;
      break;
    case GKS_K_TEXT_VALIGN_CAP:
      ay = base - cap;
      break;
    case GKS_K_TEXT_VALIGN_HALF:
      ay = (base - cap) * 0.5;
      break;
    case GKS_K_TEXT_VALIGN_BOTTOM:
      ay = base - bottom;
      break;
    default:
      ay = 0;
    }
  aspace = ay;

  gks_set_chr_xform();
  gks_chr_xform(&ax, &ay, size);

  xn = x0 + ax;
  yn = y0 + ay;
  tx[0] = xn;
  ty[0] = yn;

  for (i = 0; i < nchars; i++)
    {
      inq_text_extent(chars + i, 1, font, prec, &txx, &size, &bottom, &base, &cap, &top);

      spacex = (txx + space) * xfac[path];
      spacey = (top - bottom + space) * yfac[path];
      gks_chr_xform(&spacex, &spacey, size);

      xn += spacex;
      yn += spacey;
    }

  if ((path == GKS_K_TEXT_PATH_LEFT || path == GKS_K_TEXT_PATH_RIGHT) && alh == GKS_K_TEXT_HALIGN_CENTER)
    *cpx = x0;
  else if (alh == GKS_K_TEXT_HALIGN_RIGHT)
    *cpx = tx[0];
  else
    *cpx = xn;

  if ((path == GKS_K_TEXT_PATH_UP || path == GKS_K_TEXT_PATH_DOWN) && alv == GKS_K_TEXT_VALIGN_HALF)
    *cpy = y0;
  else if (alh == GKS_K_TEXT_HALIGN_RIGHT)
    *cpy = ty[0];
  else
    *cpy = yn;

  spacex = 0;
  spacey = -aspace;
  gks_chr_xform(&spacex, &spacey, size);
  *cpx += spacex;
  *cpy += spacey;
  NDC_to_WC(*cpx, *cpy, tnr, *cpx, *cpy);

  spacex = 0;
  spacey = bottom - base;
  gks_chr_xform(&spacex, &spacey, size);
  tx[0] += spacex;
  ty[0] += spacey;
  tx[1] = xn + spacex;
  ty[1] = yn + spacey;

  spacex = 0;
  spacey = top - bottom;
  gks_chr_xform(&spacex, &spacey, size);
  tx[2] = tx[1] + spacex;
  ty[2] = ty[1] + spacey;
  tx[3] = tx[0] + spacex;
  ty[3] = ty[0] + spacey;

  for (i = 0; i < 4; i++)
    {
      NDC_to_WC(tx[i], ty[i], tnr, tx[i], ty[i]);
    }

  gks_free(latin1_str);
}

int gks_base64(unsigned char *src, size_t srclen, char *dest, size_t destsize)
{
  size_t i, len = 0;
  unsigned char input[3];
  unsigned char output[4];

  while (srclen > 2)
    {
      input[0] = *src++;
      input[1] = *src++;
      input[2] = *src++;
      srclen -= 3;

      output[0] = input[0] >> 2;
      output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
      output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
      output[3] = input[2] & 0x3f;

      if (len + 4 > destsize) return (-1);

      dest[len++] = Base64[output[0]];
      dest[len++] = Base64[output[1]];
      dest[len++] = Base64[output[2]];
      dest[len++] = Base64[output[3]];
    }

  if (srclen != 0)
    {
      input[0] = input[1] = input[2] = '\0';
      for (i = 0; i < srclen; i++) input[i] = *src++;

      output[0] = input[0] >> 2;
      output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
      output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);

      if (len + 4 > destsize) return (-1);

      dest[len++] = Base64[output[0]];
      dest[len++] = Base64[output[1]];
      if (srclen == 1)
        dest[len++] = '=';
      else
        dest[len++] = Base64[output[2]];
      dest[len++] = '=';
    }

  if (len >= destsize) return (-1);

  dest[len] = '\0';

  return (len);
}

#ifdef _WIN32

LPSTR FAR PASCAL DLLGetEnv(LPSTR lpszVariableName)
{
  LPSTR lpEnvSearch, lpEnvBlock;
  LPSTR lpszVarSearch;
  static char *env = NULL;

  if (!*lpszVariableName) return NULL;

  lpEnvBlock = GetEnvironmentStrings();
  lpEnvSearch = lpEnvBlock;

  while (*lpEnvSearch)
    {
      /*
       *  Make a copy of the pointer to the name of the
       *  environment variable to search for.
       */
      lpszVarSearch = lpszVariableName;

      /*  Check to see if the variable names match */
      while (*lpEnvSearch && *lpszVarSearch && *lpEnvSearch == *lpszVarSearch)
        {
          lpEnvSearch++;
          lpszVarSearch++;
        }
      /*
       *  If the names match, the lpEnvSearch pointer is on the "="
       *  character and lpszVarSearch is on a null terminator.
       *  Increment and return lpszEnvSearch, which will point to the
       *  environment variable's contents.
       *
       *  If the names do not match, increment lpEnvSearch until it
       *  reaches the end of the current variable string.
       */
      if (*lpEnvSearch == '=' && *lpszVarSearch == '\0')
        {
          if (env != NULL) free(env);
          env = gks_strdup(lpEnvSearch + 1);
          FreeEnvironmentStrings(lpEnvBlock);
          return env;
        }
      else
        while (*lpEnvSearch) lpEnvSearch++;

      /*
       *  At this point the end of the environment variable's string
       *  has been reached. Increment lpEnvSearch to move to the
       *  next variable in the environment block. If it is NULL,
       *  the end of the environment block has been reached.
       */
      lpEnvSearch++;
    }

  if (lpEnvBlock != NULL) FreeEnvironmentStrings(lpEnvBlock);

  return getenv(lpszVariableName); /*
                                    * If this section of code is reached,
                                    * the variable was not found.
                                    */
}

#endif

const char *gks_getenv(const char *env)
{
#if defined(_WIN32)
  return DLLGetEnv((LPSTR)env);
#elif defined(__APPLE__)
  const char **environ, **e;
  const char *name, *value;

  if (!*env) return NULL;

  environ = (const char **)*_NSGetEnviron();
  for (e = environ; *e != NULL; e++)
    {
      name = *e;
      value = env;
      while (*name && *value && *name == *value)
        {
          name++;
          value++;
        }
      if (*name == '=' && *value == '\0') return (name + 1);
    }
  return NULL;
#else
  return getenv(env);
#endif
}

void gks_input2utf8(const char *input_str, char *utf8_str, int input_encoding)
{
  int i;
  int j;
  for (i = 0, j = 0; input_str[i] != 0; i++, j++)
    {
      switch (input_encoding)
        {
        default:
        case ENCODING_LATIN1:
          {
            size_t len;
            gks_iso2utf(input_str[i], utf8_str + j, &len);
            j += len - 1;
            break;
          }
        case ENCODING_UTF8:
          {
            utf8_str[j] = input_str[i];
            break;
          }
        }
    }
  utf8_str[j] = 0;
}

void gks_utf82latin1(const char *utf8_str, char *latin1_str)
{
  int i;
  int j;
  for (i = 0, j = 0; utf8_str[j] != 0; i++, j++)
    {
      int codepoint = 0;
      if ((utf8_str[j] & 0x80) == 0x00)
        {
          codepoint = utf8_str[j];
        }
      else if ((utf8_str[j] & 0xe0) == 0xc0 && (utf8_str[j + 1] & 0xc0) == 0x80)
        {
          codepoint = ((utf8_str[j] & 0x1f) << 6) + (utf8_str[j + 1] & 0x3f);
          j += 1;
        }
      else if ((utf8_str[j] & 0xf0) == 0xe0 && (utf8_str[j + 1] & 0xc0) == 0x80 && (utf8_str[j + 2] & 0xc0) == 0x80)
        {
          codepoint = ((utf8_str[j] & 0xf) << 12) + ((utf8_str[j + 1] & 0x3f) << 6) + (utf8_str[j + 1] & 0x3f);
          j += 2;
        }
      else if ((utf8_str[j] & 0xf8) == 0xf0 && (utf8_str[j + 1] & 0xc0) == 0x80 && (utf8_str[j + 2] & 0xc0) == 0x80 &&
               (utf8_str[j + 3] & 0xc0) == 0x80)
        {
          codepoint = ((utf8_str[j] & 0x7) << 18) + ((utf8_str[j + 1] & 0x3f) << 12) + ((utf8_str[j + 2] & 0x3f) << 6) +
                      (utf8_str[j + 3] & 0x3f);
          j += 3;
        }
      else
        {
          /* invalid byte combination */
          i -= 1;
          continue;
        }
      if (codepoint < 256)
        {
          latin1_str[i] = codepoint;
        }
      else if (codepoint == 8722)
        {
          latin1_str[i] = '-';
        }
      else
        {
          latin1_str[i] = '?';
        }
    }
  latin1_str[i] = 0;
}

void gks_iso2utf(unsigned char c, char *utf, size_t *len)
{
  if (c < 0x80)
    {
      *len = 1;
      utf[0] = c;
    }
  else
    {
      *len = 2;
      utf[1] = 0x80 | (c & 0x3f);
      c >>= 6;
      utf[0] = 0xc0 | (c & 0x1f);
    }
}

void gks_symbol2utf(unsigned char c, char *utf, size_t *len)
{
  *len = strlen(symbol2utf[c]);
  memmove(utf, symbol2utf[c], *len);
}

int *gks_resize(int *image, int width, int height, int w, int h)
{
  int x_ratio = (int)((width << 16) / w);
  int y_ratio = (int)((height << 16) / h);
  int *result, i, j, x, y;

  result = (int *)malloc(w * h * sizeof(int));
  for (i = 0; i < h; i++)
    {
      for (j = 0; j < w; j++)
        {
          x = ((j * x_ratio) >> 16);
          y = ((i * y_ratio) >> 16);
          result[(i * w) + j] = image[(y * width) + x];
        }
    }
  return result;
}

void gks_filepath(char *path, char *defpath, const char *type, int page, int index)
{
  char number[20], *p;
  const char *env;

  env = gks_getenv("GKS_FILEPATH");
  if (defpath != NULL)
    strcpy(path, defpath);
  else if (env != NULL)
    strcpy(path, env);
  else
    strcpy(path, "gks");

  p = strrchr(path, '.');
  if (p) *p = '\0';

  if (page > 1 && !gks_getenv("GKS_DISABLE_PAGE_SUFFIX"))
    {
      strcat(path, "-");
      snprintf(number, 20, "%d", page);
      strcat(path, number);
    }
  if (index != 0)
    {
      strcat(path, "_");
      snprintf(number, 20, "%d", index);
      strcat(path, number);
    }
  strcat(path, ".");
  strcat(path, type);
}

void gks_adjust_cellarray(double *qx, double *qy, double *rx, double *ry, int *scol, int *srow, int *ncol, int *nrow,
                          int dimx, int dimy)
{
  double xmin, xmax, ymin, ymax, tmp, dx, dy;

  WC_to_NDC(*qx, *qy, gkss->cntnr, xmin, ymax);
  WC_to_NDC(*rx, *ry, gkss->cntnr, xmax, ymin);

  if (*rx < *qx)
    {
      tmp = xmax;
      xmax = xmin;
      xmin = tmp;
    }
  if (*ry < *qy)
    {
      tmp = ymax;
      ymax = ymin;
      ymin = tmp;
    }

  dx = (xmax - xmin) / *ncol;
  dy = (ymax - ymin) / *nrow;

  while (xmin + dx < 0 && *ncol > 0)
    {
      xmin += dx;
      *scol += 1;
      *ncol -= 1;
      if (xmin >= xmax || *scol + *ncol - 1 > dimx) *ncol = 0;
    }
  while (xmax - dx > 1 && *ncol > 0)
    {
      xmax -= dx;
      *ncol -= 1;
      if (xmin >= xmax) *ncol = 0;
    }

  while (ymin + dy < 0 && *ncol > 0 && *nrow > 0)
    {
      ymin += dy;
      *srow += 1;
      *nrow -= 1;
      if (ymin >= ymax || *srow + *nrow - 1 > dimy) *nrow = 0;
    }
  while (ymax - dy > 1 && *ncol > 0 && *nrow > 0)
    {
      ymax -= dy;
      *nrow -= 1;
      if (ymin >= ymax) *nrow = 0;
    }

  if (xmax - xmin > 3 || ymax - ymin > 3) *ncol = *nrow = 0;

  if (*rx < *qx)
    {
      tmp = xmax;
      xmax = xmin;
      xmin = tmp;
    }
  if (*ry < *qy)
    {
      tmp = ymax;
      ymax = ymin;
      ymin = tmp;
    }

  NDC_to_WC(xmin, ymax, gkss->cntnr, *qx, *qy);
  NDC_to_WC(xmax, ymin, gkss->cntnr, *rx, *ry);
}

char *gks_strdup(const char *str)
{
  if (str)
    {
      char *dup = gks_malloc(strlen(str) + 1);
      strcpy(dup, str);
      return dup;
    }
  else
    {
      return NULL;
    }
}
